code wiki / (root) / nx_hmac_sha1.nx

nx_hmac_sha1.nx source

↩ module page · 109 lines · 3391 B

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