code wiki / (root) / hmac_sha1.nx

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}