hmac_sha1.nx source
↩ module page · 100 lines · 3132 B
1// hmac_sha1.nx -- HMAC with SHA-1 (RFC 2104 + FIPS 198-1).
2//
3// Interop-only primitive, like sha1.nx itself. HMAC does not
4// rely on its inner hash's collision resistance (only pseudo-
5// randomness of the keyed mix), so HMAC-SHA1 remains safe for
6// authentication even though plain SHA-1 is broken for signatures.
7//
8// Still widely used:
9// - TOTP / HOTP (RFC 4226 / 6238 default)
10// - OAuth 1.0 HMAC-SHA1 signatures
11// - AWS SigV2 / legacy API auth
12// - PBKDF2-HMAC-SHA1 (WPA2, older WebCrypto key derivation)
13// - Older JWT HS1 tokens
14//
15// Algorithm (RFC 2104):
16// block_size = 64 bytes for SHA-1
17// if len(key) > block_size: key = SHA1(key)
18// key = key || zeros to block_size
19// ipad = key XOR 0x36 repeated
20// opad = key XOR 0x5C repeated
21// tag = SHA1(opad || SHA1(ipad || msg))
22//
23// Composes sha1.nx. Output is 20 bytes.
24//
25// Invariants:
26// HS1 Output = 20 bytes always (HMAC-SHA1 tag length).
27// HS2 Matches RFC 2202 test vectors (not checked in smoke,
28// but the algorithm is textbook).
29
30import "syscalls.nx"
31import "nx_sha1.nx"
32
33const HMAC_SHA1_BLOCK: i64 = 64
34const HMAC_SHA1_OUT: i64 = 20
35
36func hmac_sha1(key: *u8, key_len: i64,
37 msg: *u8, msg_len: i64,
38 out: *u8) -> i64 {
39 let k_prime: *u8 = sys_mmap(HMAC_SHA1_BLOCK + 16)
40 var i: i64 = 0
41 while i < HMAC_SHA1_BLOCK { k_prime[i] = 0; i = i + 1 }
42
43 if key_len > HMAC_SHA1_BLOCK {
44 // Shorten long keys by hashing.
45 sha1(key, key_len, k_prime)
46 } else {
47 i = 0
48 while i < key_len {
49 k_prime[i] = key[i]
50 i = i + 1
51 }
52 }
53
54 // Inner hash: SHA1(ipad || msg).
55 let inner_buf_len: i64 = HMAC_SHA1_BLOCK + msg_len
56 let inner_buf: *u8 = sys_mmap(inner_buf_len + 16)
57 i = 0
58 while i < HMAC_SHA1_BLOCK {
59 inner_buf[i] = k_prime[i] ^ 0x36
60 i = i + 1
61 }
62 i = 0
63 while i < msg_len {
64 inner_buf[HMAC_SHA1_BLOCK + i] = msg[i]
65 i = i + 1
66 }
67 let inner_hash: *u8 = sys_mmap(32)
68 sha1(inner_buf, inner_buf_len, inner_hash)
69
70 // Outer hash: SHA1(opad || inner_hash).
71 let outer_buf_len: i64 = HMAC_SHA1_BLOCK + HMAC_SHA1_OUT
72 let outer_buf: *u8 = sys_mmap(outer_buf_len + 16)
73 i = 0
74 while i < HMAC_SHA1_BLOCK {
75 outer_buf[i] = k_prime[i] ^ 0x5C
76 i = i + 1
77 }
78 i = 0
79 while i < HMAC_SHA1_OUT {
80 outer_buf[HMAC_SHA1_BLOCK + i] = inner_hash[i]
81 i = i + 1
82 }
83 sha1(outer_buf, outer_buf_len, out)
84 return 0
85}
86
87// Compile-only smoke.
88func main() -> i64 {
89 let out: *u8 = sys_mmap(32)
90 hmac_sha1("key", 3, "The quick brown fox jumps over the lazy dog", 43, out)
91 // RFC 2202 test vector:
92 // HMAC-SHA1(key=\"key\", msg=\"The quick brown fox jumps over the lazy dog\")
93 // = de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9
94 if out[0] != 0xDE { return 1 }
95 if out[1] != 0x7C { return 2 }
96 if out[2] != 0x9B { return 3 }
97 if out[3] != 0x85 { return 4 }
98 if out[19] != 0xD9 { return 5 }
99 return 0
100}