From 93aa7e3aa291f3328cf34a5a2f86172f41d0affd Mon Sep 17 00:00:00 2001 From: cirakg Date: Sun, 18 Oct 2026 23:44:18 +0200 Subject: test: add cross-checked crypto/OTP vectors, unit tests and stricter screenshots - gen_vectors.py: RFC/FIPS/NIST vectors recomputed with Python's stdlib (openssl for AES) before use, plus seeded cases around SHA padding boundaries, 2038 and 2100; generates test/data/Vectors.mc. - 31 Monkey C tests (hashes, HMAC, PBKDF2, HKDF, AES, sealed records incl. every-byte tamper detection, Base32 errors, 100 OTP and 12 Steam vectors, clock, jobs) and a benchmark; pass on all four test devices. - Screenshot tolerance 0.01 %: one changed character no longer passes. - Golden images updated for version 0.2.0. Co-Authored-By: Claude Opus 5.5 --- test/crypto/SealedBoxTest.mc | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 test/crypto/SealedBoxTest.mc (limited to 'test/crypto/SealedBoxTest.mc') diff --git a/test/crypto/SealedBoxTest.mc b/test/crypto/SealedBoxTest.mc new file mode 100644 index 0000000..810e512 --- /dev/null +++ b/test/crypto/SealedBoxTest.mc @@ -0,0 +1,96 @@ +// Author: cirakg & claude ai +// Summary: Tests for SealedBox (encrypt-then-MAC records): round trips, tamper detection, wrong key or aad, and the self-test. + +import Toybox.Lang; +import Toybox.Test; + +(:test) +module SealedBoxFixtures { + function keys(seed as Number) as SealedBox.Keys { + var master = new [32]b; + for (var i = 0; i < 32; i++) { + master[i] = (i * 13 + seed) & 0xFF; + } + return new SealedBox.Keys(master); + } +} + +(:test) +function sealedBoxRoundTrips(logger as Test.Logger) as Boolean { + var keys = SealedBoxFixtures.keys(1); + var aad = Bytes.fromUtf8("K7F2|header"); + for (var length = 0; length <= 70; length += 7) { + var plain = new [length]b; + for (var i = 0; i < length; i++) { + plain[i] = (i * 31) & 0xFF; + } + var box = SealedBox.seal(keys, aad, plain); + var back = SealedBox.open(keys, aad, box); + if (back == null || !Bytes.equal(back, plain) || box[0] != SealedBox.VERSION) { + logger.error("round trip failed at length " + length); + return false; + } + } + return true; +} + +// Flipping any single byte of the box (version, IV, ciphertext or tag) must +// make open() return null. +(:test) +function sealedBoxDetectsEveryTamperedByte(logger as Test.Logger) as Boolean { + var keys = SealedBoxFixtures.keys(2); + var aad = [9, 9]b; + var box = SealedBox.seal(keys, aad, Bytes.fromUtf8("JBSWY3DPEHPK3PXP fake secret")); + for (var i = 0; i < box.size(); i++) { + var tampered = Bytes.copy(box); + tampered[i] = tampered[i] ^ 0x01; + if (SealedBox.open(keys, aad, tampered) != null) { + logger.error("tampering byte " + i + " was not detected"); + return false; + } + } + return true; +} + +(:test) +function sealedBoxRejectsWrongKeyAadAndTruncation(logger as Test.Logger) as Boolean { + var keys = SealedBoxFixtures.keys(3); + var aad = [1, 2, 3]b; + var box = SealedBox.seal(keys, aad, [42]b); + // Moving a byte from the aad into the box must not verify either. + var shifted = Bytes.concat([3]b, box); + return SealedBox.open(SealedBoxFixtures.keys(4), aad, box) == null + && SealedBox.open(keys, [1, 2]b, box) == null + && SealedBox.open(keys, [1, 2]b, shifted) == null + && SealedBox.open(keys, aad, box.slice(0, box.size() - 1)) == null + && SealedBox.open(keys, aad, []b) == null + && SealedBox.open(keys, aad, box) != null; +} + +// The same IV gives the same box; random IVs make every seal different. +(:test) +function sealedBoxIvHandling(logger as Test.Logger) as Boolean { + var keys = SealedBoxFixtures.keys(5); + var iv = new [16]b; + var a = SealedBox.sealWithIv(keys, []b, [7]b, iv); + var b = SealedBox.sealWithIv(keys, []b, [7]b, iv); + var c = SealedBox.seal(keys, []b, [7]b); + var d = SealedBox.seal(keys, []b, [7]b); + return Bytes.equal(a, b) && !Bytes.equal(c, d); +} + +(:test) +function sealedBoxKeysWipe(logger as Test.Logger) as Boolean { + var keys = SealedBoxFixtures.keys(6); + keys.wipe(); + return Bytes.equal(keys.encKey, new [32]b) && Bytes.equal(keys.macKey, new [32]b); +} + +(:test) +function selfTestPasses(logger as Test.Logger) as Boolean { + var failed = SelfTest.run(); + if (failed != null) { + logger.error("self-test failed: " + failed); + } + return failed == null; +} -- cgit v1.2.3