code wiki / (root) / hmac_sha1.nx

hmac_sha1.nx source

↩ module page · 121 lines · 4820 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" 31// 2026-08-01 -- REPOINTED FROM sha1.nx TO nx_sha1.nx. THIS ONE LINE IS THE sev-8 REMEDY. 32// 33// sha1.nx COMPUTES WRONG DIGESTS: sha1("abc") -> 35 04 .. against RFC 3174's published A9 99 3E 36. 34// Proven two ways, by two unrelated authorities: 35// RFC 3174 -- nx_sha1_extvec_gate (nx_sha1.nx) GREEN vs nx_sha1alt_extvec_gate (sha1.nx) RED 36// RFC 2202 -- 0/9 via sha1.nx vs 9/9 via nx_sha1.nx, changing ONLY the hash core 37// RFC 5869 -- nx_hkdf7_extvec_gate 7/7 vs nx_hkdf7alt_extvec_gate failing A.4-A.7, with the SHA-256 38// cases passing in BOTH arms as a control that must not move 39// 40// EVERY dependent reaches the core THROUGH THIS IMPORT, so repointing here fixes them all at once: 41// hkdf_sha1.nx · hotp_sha1.nx · totp_sha1.nx · pbkdf2_sha1.nx (and totp_verify.nx transitively) 42// i.e. HMAC-SHA-1, HKDF-SHA-1, HOTP, TOTP two-factor codes, and PBKDF2 password derivation were ALL 43// producing incorrect values. ★COUNT THE IMPORT CLOSURE, NOT THE FILES YOU HAPPENED TO GREP -- I filed 44// this defect twice with the wrong blast radius (3, then 5) before reading the closure and finding 6. 45// 46// SAFE AS A DROP-IN, VERIFIED BEFORE EDITING rather than assumed: nx_sha1.nx exports the SAME symbol set 47// (sha1, sha1_rotl, sha1_process_block, if_ge, SHA1_H0..K3, SHA1_MASK32) and is a strict SUPERSET (adds 48// sha1_into + its scratch constants). No caller signature changes. ★A DROP-IN REPLACEMENT IS A CLAIM ABOUT 49// SYMBOLS -- CHECK THE SYMBOL SETS, DO NOT INFER IT FROM THE FILENAMES BEING SIMILAR. 50// ⚠sha1.nx is left in the tree ON PURPOSE so nx_sha1alt_extvec_gate can keep convicting it; retiring it is 51// a separate, later step. 52import "nx_sha1.nx" 53 54const HMAC_SHA1_BLOCK: i64 = 64 55const HMAC_SHA1_OUT: i64 = 20 56 57func hmac_sha1(key: *u8, key_len: i64, 58 msg: *u8, msg_len: i64, 59 out: *u8) -> i64 { 60 let k_prime: *u8 = sys_mmap(HMAC_SHA1_BLOCK + 16) 61 var i: i64 = 0 62 while i < HMAC_SHA1_BLOCK { k_prime[i] = 0; i = i + 1 } 63 64 if key_len > HMAC_SHA1_BLOCK { 65 // Shorten long keys by hashing. 66 sha1(key, key_len, k_prime) 67 } else { 68 i = 0 69 while i < key_len { 70 k_prime[i] = key[i] 71 i = i + 1 72 } 73 } 74 75 // Inner hash: SHA1(ipad || msg). 76 let inner_buf_len: i64 = HMAC_SHA1_BLOCK + msg_len 77 let inner_buf: *u8 = sys_mmap(inner_buf_len + 16) 78 i = 0 79 while i < HMAC_SHA1_BLOCK { 80 inner_buf[i] = k_prime[i] ^ 0x36 81 i = i + 1 82 } 83 i = 0 84 while i < msg_len { 85 inner_buf[HMAC_SHA1_BLOCK + i] = msg[i] 86 i = i + 1 87 } 88 let inner_hash: *u8 = sys_mmap(32) 89 sha1(inner_buf, inner_buf_len, inner_hash) 90 91 // Outer hash: SHA1(opad || inner_hash). 92 let outer_buf_len: i64 = HMAC_SHA1_BLOCK + HMAC_SHA1_OUT 93 let outer_buf: *u8 = sys_mmap(outer_buf_len + 16) 94 i = 0 95 while i < HMAC_SHA1_BLOCK { 96 outer_buf[i] = k_prime[i] ^ 0x5C 97 i = i + 1 98 } 99 i = 0 100 while i < HMAC_SHA1_OUT { 101 outer_buf[HMAC_SHA1_BLOCK + i] = inner_hash[i] 102 i = i + 1 103 } 104 sha1(outer_buf, outer_buf_len, out) 105 return 0 106} 107 108// Compile-only smoke. 109func main() -> i64 { 110 let out: *u8 = sys_mmap(32) 111 hmac_sha1("key", 3, "The quick brown fox jumps over the lazy dog", 43, out) 112 // RFC 2202 test vector: 113 // HMAC-SHA1(key=\"key\", msg=\"The quick brown fox jumps over the lazy dog\") 114 // = de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 115 if out[0] != 0xDE { return 1 } 116 if out[1] != 0x7C { return 2 } 117 if out[2] != 0x9B { return 3 } 118 if out[3] != 0x85 { return 4 } 119 if out[19] != 0xD9 { return 5 } 120 return 0 121}