From e47ae684101981a7c20f3270994a61944059c825 Mon Sep 17 00:00:00 2001 From: cirakg Date: Sun, 18 Oct 2026 23:44:18 +0200 Subject: feat(crypto): add SHA-512, HMAC, PBKDF2, HKDF, AES-256 records and self-test - Sha512: FIPS 180-4 in Monkey C (no native SHA-512), rotations inlined with precomputed masks; constants derived from the primes, not typed. - Hmac: RFC 2104 over SHA-1/256/512 (native HMAC only supports SHA-256). - Kdf: resumable PBKDF2-HMAC-SHA256 job with calibration bounds (2,000-200,000 iterations) and HKDF-SHA256. - Aes: CBC with PKCS#7 over the native cipher; AES-256 chosen. - SealedBox: encrypt-then-MAC records (HKDF-split keys, length-prefixed aad, constant-time tag check before decryption). - SelfTest: known-answer tests for every primitive at start-up. - Bytes (wipe, constant-time equal), Clock (2038-safe widening, trusted clock), Jobs (bounded steps against the watchdog, timer runner). Co-Authored-By: Claude Opus 5.5 --- source/crypto/Hashes.mc | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 source/crypto/Hashes.mc (limited to 'source/crypto/Hashes.mc') diff --git a/source/crypto/Hashes.mc b/source/crypto/Hashes.mc new file mode 100644 index 0000000..14e2cf2 --- /dev/null +++ b/source/crypto/Hashes.mc @@ -0,0 +1,54 @@ +// Author: cirakg & claude ai +// Summary: One interface over SHA-1, SHA-256 (native) and SHA-512 (pure Monkey C). +// +// Connect IQ has native SHA-1 and SHA-256 (Toybox.Cryptography.Hash) but no +// SHA-512, which source/crypto/Sha512.mc implements from FIPS 180-4. OTP code +// asks for an algorithm by its Hashes.Algorithm value and doesn't care which +// implementation answers. + +import Toybox.Cryptography; +import Toybox.Lang; + +module Hashes { + + // OTP and HMAC hash algorithms. The values are stored in entry records, so + // never renumber them. + enum Algorithm { + SHA1 = 0, + SHA256 = 1, + SHA512 = 2 + } + + // Input block size in bytes (what HMAC pads its key to). + function blockSize(alg as Algorithm) as Number { + return alg == SHA512 ? 128 : 64; + } + + // Digest length in bytes. + function digestSize(alg as Algorithm) as Number { + if (alg == SHA1) { + return 20; + } + return alg == SHA256 ? 32 : 64; + } + + // True for the three supported values (input from settings or imports is + // checked with this before it becomes an Algorithm). + function isValid(value as Number) as Boolean { + return value == SHA1 || value == SHA256 || value == SHA512; + } + + // The digest of data. + function digest(alg as Algorithm, data as ByteArray) as ByteArray { + if (alg == SHA512) { + return Sha512.digest(data); + } + var hash = new Cryptography.Hash({ + :algorithm => alg == SHA1 ? Cryptography.HASH_SHA1 : Cryptography.HASH_SHA256 + }); + if (data.size() > 0) { + hash.update(data); + } + return hash.digest(); + } +} -- cgit v1.2.3