// Author: cirakg & claude ai // Summary: Benchmarks of the crypto, OTP and storage code; logs BENCH lines for docs/PERFORMANCE.md, never fails. // // Simulator timings run on the PC's CPU, so they only compare primitives with // each other (for example how much SHA-512 costs against native SHA-1). // Watch timings come from the on-watch benchmark in M4. import Toybox.Lang; import Toybox.System; import Toybox.Test; (:test) module Bench { // Run fn `times` times and log the average in microseconds. function measure(logger as Test.Logger, name as String, times as Number, fn as Method() as Void) as Void { var start = System.getTimer(); for (var i = 0; i < times; i++) { fn.invoke(); } var elapsed = System.getTimer() - start; logger.debug("BENCH " + name + " us_per_call=" + (elapsed * 1000 / times)); } class Cases { var secret20 as ByteArray = new [20]b; var secret64 as ByteArray = new [64]b; var keys as SealedBox.Keys = new SealedBox.Keys(new [32]b); var box as ByteArray = []b; function initialize() { box = SealedBox.seal(keys, []b, new [20]b); } function totpSha1() as Void { Otp.totp(Hashes.SHA1, secret20, 1700000000l, 30, 6); } function totpSha256() as Void { Otp.totp(Hashes.SHA256, secret20, 1700000000l, 30, 6); } function totpSha512() as Void { Otp.totp(Hashes.SHA512, secret64, 1700000000l, 30, 6); } function pbkdf2Thousand() as Void { Kdf.pbkdf2([1, 2, 3, 4, 5, 6]b, new [16]b, 1000, 32); } function sealOpen() as Void { SealedBox.open(keys, []b, SealedBox.seal(keys, []b, new [20]b)); } function selfTest() as Void { SelfTest.run(); } } // A vault with 20 OTP entries, for list and open timings. class VaultCases { var vault as Vault; var ids as Array; function initialize() { vault = new Vault(new MemoryBackend(), new FakeTime()); for (var i = 0; i < 20; i++) { vault.add(StorageFixtures.totp("Entry " + i)); } ids = vault.ids(); } // What the list does on start-up: every header. function readAllHeaders() as Void { for (var i = 0; i < ids.size(); i++) { vault.header(ids[i]); } } // What showing one code does: decrypt one entry. function openOne() as Void { vault.open(ids[0]).wipeSecrets(); } function addOne() as Void { var e = StorageFixtures.totp("Bench"); vault.add(e); vault.remove(e.id); } } } (:test) function benchmarkPrimitives(logger as Test.Logger) as Boolean { var c = new Bench.Cases(); Bench.measure(logger, "totp_sha1", 20, c.method(:totpSha1)); Bench.measure(logger, "totp_sha256", 20, c.method(:totpSha256)); Bench.measure(logger, "totp_sha512", 20, c.method(:totpSha512)); Bench.measure(logger, "pbkdf2_1000_iterations", 3, c.method(:pbkdf2Thousand)); Bench.measure(logger, "sealedbox_seal_open", 20, c.method(:sealOpen)); Bench.measure(logger, "self_test", 3, c.method(:selfTest)); return true; } (:test) function benchmarkVault(logger as Test.Logger) as Boolean { var v = new Bench.VaultCases(); Bench.measure(logger, "vault_20_headers", 5, v.method(:readAllHeaders)); Bench.measure(logger, "vault_open_entry", 20, v.method(:openOne)); Bench.measure(logger, "vault_add_remove", 10, v.method(:addOne)); return true; }