1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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;
}
|