diff options
Diffstat (limited to 'source/crypto')
| -rw-r--r-- | source/crypto/Aes.mc | 2 | ||||
| -rw-r--r-- | source/crypto/Hashes.mc | 2 | ||||
| -rw-r--r-- | source/crypto/Hkdf.mc | 39 | ||||
| -rw-r--r-- | source/crypto/Hmac.mc | 2 | ||||
| -rw-r--r-- | source/crypto/Kdf.mc | 33 | ||||
| -rw-r--r-- | source/crypto/SealedBox.mc | 6 | ||||
| -rw-r--r-- | source/crypto/SelfTest.mc | 2 | ||||
| -rw-r--r-- | source/crypto/Sha512.mc | 2 |
8 files changed, 57 insertions, 31 deletions
diff --git a/source/crypto/Aes.mc b/source/crypto/Aes.mc index e77cb86..242448b 100644 --- a/source/crypto/Aes.mc +++ b/source/crypto/Aes.mc @@ -15,6 +15,8 @@ import Toybox.Cryptography; import Toybox.Lang; +// (:glance): also loaded by the glance, which shows the favourite code (GlanceCode). +(:glance) module Aes { const BLOCK_BYTES = 16; diff --git a/source/crypto/Hashes.mc b/source/crypto/Hashes.mc index 14e2cf2..321ea2d 100644 --- a/source/crypto/Hashes.mc +++ b/source/crypto/Hashes.mc @@ -9,6 +9,8 @@ import Toybox.Cryptography; import Toybox.Lang; +// (:glance): also loaded by the glance, which shows the favourite code (GlanceCode). +(:glance) module Hashes { // OTP and HMAC hash algorithms. The values are stored in entry records, so diff --git a/source/crypto/Hkdf.mc b/source/crypto/Hkdf.mc new file mode 100644 index 0000000..97814b9 --- /dev/null +++ b/source/crypto/Hkdf.mc @@ -0,0 +1,39 @@ +// Author: cirakg & claude ai +// Summary: HKDF-SHA256 (RFC 5869) on the native HMAC, small enough for the glance. +// +// The key derivation SealedBox and KeyManager use for every sub-key. It +// lives apart from Kdf (PBKDF2, which needs the job system) so the glance +// can open the sealed favourite code (GlanceCode) with only the crypto it +// needs. Verified with RFC 5869 test cases 1 to 3 (test/crypto/KdfAesTest.mc) +// and in the start-up self-test. + +import Toybox.Lang; + +(:glance) +module Hkdf { + + const HASH_BYTES = 32; + + // Extract with salt (empty means 32 zero bytes), then expand to + // `length` bytes with info. length <= 255 * 32. + function derive(ikm as ByteArray, salt as ByteArray, info as ByteArray, length as Number) as ByteArray { + if (length < 1 || length > 255 * HASH_BYTES) { + throw new Lang.InvalidValueException("HKDF: bad key length"); + } + var prk = Hmac.nativeSha256(salt.size() > 0 ? salt : new [HASH_BYTES]b, ikm); + var okm = new [0]b; + var block = new [0]b; + for (var counter = 1; okm.size() < length; counter++) { + var input = Bytes.concat(block, info); + input.add(counter); + Bytes.wipe(block); + block = Hmac.nativeSha256(prk, input); + okm.addAll(block); + } + Bytes.wipe(prk); + Bytes.wipe(block); + var key = okm.slice(0, length); + Bytes.wipe(okm); + return key; + } +} diff --git a/source/crypto/Hmac.mc b/source/crypto/Hmac.mc index ff620a4..736d2d4 100644 --- a/source/crypto/Hmac.mc +++ b/source/crypto/Hmac.mc @@ -16,6 +16,8 @@ import Toybox.Cryptography; import Toybox.Lang; +// (:glance): also loaded by the glance, which shows the favourite code (GlanceCode). +(:glance) module Hmac { const IPAD = 0x36; diff --git a/source/crypto/Kdf.mc b/source/crypto/Kdf.mc index 5bace1c..e64c8ff 100644 --- a/source/crypto/Kdf.mc +++ b/source/crypto/Kdf.mc @@ -1,5 +1,5 @@ // Author: cirakg & claude ai -// Summary: Key derivation: resumable PBKDF2-HMAC-SHA256 (RFC 8018) with calibration, and HKDF-SHA256 (RFC 5869). +// Summary: PIN key derivation: resumable PBKDF2-HMAC-SHA256 (RFC 8018) with calibration. // // PBKDF2 turns the PIN into a key (README 7.2). It runs as a Jobs.Job, a // bounded number of iterations per step, because the iteration count is @@ -7,11 +7,11 @@ // watchdog budget. It uses the native HMAC-SHA256: the object keeps the key // and digest() resets it, so each iteration is one native call. // -// HKDF splits one master key into separate encryption and MAC keys -// (README 7.3). +// HKDF, which splits one master key into separate encryption and MAC keys +// (README 7.3), is in Hkdf.mc (the glance needs it, but not PBKDF2). // -// Verified with RFC 7914 section 11 (PBKDF2) and RFC 5869 A.1-A.3 (HKDF), -// plus seeded vectors from Python's hashlib (test/crypto/KdfTest.mc). +// Verified with RFC 7914 section 11 (PBKDF2) plus seeded vectors from +// Python's hashlib (test/crypto/KdfAesTest.mc). import Toybox.Cryptography; import Toybox.Lang; @@ -191,27 +191,4 @@ module Kdf { } return estimate > MAX_ITERATIONS ? MAX_ITERATIONS : estimate.toNumber(); } - - // HKDF-SHA256 (RFC 5869): extract with salt (empty means 32 zero bytes), - // then expand to `length` bytes with info. length <= 255 * 32. - function hkdf(ikm as ByteArray, salt as ByteArray, info as ByteArray, length as Number) as ByteArray { - if (length < 1 || length > 255 * SHA256_BYTES) { - throw new Lang.InvalidValueException("HKDF: bad key length"); - } - var prk = Hmac.nativeSha256(salt.size() > 0 ? salt : new [SHA256_BYTES]b, ikm); - var okm = new [0]b; - var block = new [0]b; - for (var counter = 1; okm.size() < length; counter++) { - var input = Bytes.concat(block, info); - input.add(counter); - Bytes.wipe(block); - block = Hmac.nativeSha256(prk, input); - okm.addAll(block); - } - Bytes.wipe(prk); - Bytes.wipe(block); - var key = okm.slice(0, length); - Bytes.wipe(okm); - return key; - } } diff --git a/source/crypto/SealedBox.mc b/source/crypto/SealedBox.mc index 0804b96..765a1d8 100644 --- a/source/crypto/SealedBox.mc +++ b/source/crypto/SealedBox.mc @@ -20,6 +20,8 @@ import Toybox.Cryptography; import Toybox.Lang; +// (:glance): also loaded by the glance, which shows the favourite code (GlanceCode). +(:glance) module SealedBox { const VERSION = 1; @@ -34,8 +36,8 @@ module SealedBox { var macKey as ByteArray; function initialize(masterKey as ByteArray) { - encKey = Kdf.hkdf(masterKey, []b, Bytes.fromUtf8("OTPW enc v1"), KEY_BYTES); - macKey = Kdf.hkdf(masterKey, []b, Bytes.fromUtf8("OTPW mac v1"), KEY_BYTES); + encKey = Hkdf.derive(masterKey, []b, Bytes.fromUtf8("OTPW enc v1"), KEY_BYTES); + macKey = Hkdf.derive(masterKey, []b, Bytes.fromUtf8("OTPW mac v1"), KEY_BYTES); } // Wipe both keys (when the session locks). diff --git a/source/crypto/SelfTest.mc b/source/crypto/SelfTest.mc index 0d7eae4..87515b6 100644 --- a/source/crypto/SelfTest.mc +++ b/source/crypto/SelfTest.mc @@ -54,7 +54,7 @@ module SelfTest { for (var i = 0; i < 22; i++) { ikm[i] = 0x0b; } - if (!check(Kdf.hkdf(ikm, []b, []b, 42), "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8")) { // lint: allow-literal + if (!check(Hkdf.derive(ikm, []b, []b, 42), "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8")) { // lint: allow-literal return "HKDF"; } // RFC 4226 Appendix D, counter 0. diff --git a/source/crypto/Sha512.mc b/source/crypto/Sha512.mc index 4248462..37e6607 100644 --- a/source/crypto/Sha512.mc +++ b/source/crypto/Sha512.mc @@ -16,6 +16,8 @@ import Toybox.Lang; +// (:glance): also loaded by the glance, which shows the favourite code (GlanceCode). +(:glance) module Sha512 { // Round constants: the first 64 bits of the fractional parts of the cube |
