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
|
// Author: cirakg & claude ai
// Summary: HMAC (RFC 2104) over SHA-1, SHA-256 and SHA-512; native HMAC-SHA256 where Connect IQ provides it.
//
// Connect IQ's native HMAC accepts only SHA-256 (HMAC-SHA1 is rejected with
// "Invalid :algorithm option value", verified with SDK 9.2.0). Most TOTP
// secrets use SHA-1, so compute() builds HMAC from the hash functions as
// RFC 2104 defines it:
// HMAC(K, m) = H((K' xor opad) || H((K' xor ipad) || m))
// where K' is the key padded to the hash's block size (hashed first if it is
// longer than a block), ipad = 0x36 and opad = 0x5c repeated.
//
// Security notes: the padded key copies are wiped after use; the caller's key
// array is never modified. Verified with RFC 2202 (SHA-1) and RFC 4231
// (SHA-256, SHA-512), test/crypto/HmacTest.mc.
import Toybox.Cryptography;
import Toybox.Lang;
// (:glance): also loaded by the glance, which shows the favourite code (GlanceCode).
(:glance)
module Hmac {
const IPAD = 0x36;
const OPAD = 0x5c;
// HMAC of message under key. SHA-256 uses the native implementation; SHA-1
// and SHA-512 use generic().
function compute(alg as Hashes.Algorithm, key as ByteArray, message as ByteArray) as ByteArray {
if (alg == Hashes.SHA256) {
return nativeSha256(key, message);
}
return generic(alg, key, message);
}
// RFC 2104 HMAC built on Hashes.digest, for any supported algorithm.
function generic(alg as Hashes.Algorithm, key as ByteArray, message as ByteArray) as ByteArray {
var block = Hashes.blockSize(alg);
var hashedKey = key.size() > block ? Hashes.digest(alg, key) : null;
var k = hashedKey != null ? hashedKey : key;
var inner = new [block]b;
var outer = new [block]b;
for (var i = 0; i < block; i++) {
var keyByte = i < k.size() ? k[i] : 0;
inner[i] = keyByte ^ IPAD;
outer[i] = keyByte ^ OPAD;
}
inner.addAll(message);
var innerHash = Hashes.digest(alg, inner);
outer.addAll(innerHash);
var mac = Hashes.digest(alg, outer);
Bytes.wipe(inner);
Bytes.wipe(outer);
Bytes.wipe(innerHash);
Bytes.wipe(hashedKey);
return mac;
}
// Native HMAC-SHA256 (Toybox.Cryptography).
function nativeSha256(key as ByteArray, message as ByteArray) as ByteArray {
var mac = new Cryptography.HashBasedMessageAuthenticationCode({
:algorithm => Cryptography.HASH_SHA256,
:key => key
});
if (message.size() > 0) {
mac.update(message);
}
return mac.digest();
}
}
|