code wiki / (root) / nx_etag.nx

nx_etag.nx source

↩ module page · 161 lines · 5777 B

1// etag.nx -- generate + compare HTTP ETag headers (RFC 7232). 2// 3// The ETag header identifies a specific version of a resource so 4// conditional requests (If-None-Match / If-Match) can avoid 5// re-sending bytes that haven't changed. Every static-file server 6// needs this, every CDN lives or dies on it. 7// 8// Format (RFC 7232 §2.3): 9// ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" <- strong 10// ETag: W/"3ad4a9c3" <- weak 11// 12// Strong ETags mean byte-exact match; weak (W/-prefixed) means 13// semantically equivalent but possibly byte-different (e.g. same 14// content reflowed whitespace). 15// 16// We generate ETags by: 17// 1. sha256 (existing) of the file contents -> 32 bytes 18// 2. hex-encode (existing) -> 64 ASCII chars 19// 3. truncate to etag_size bytes (caller choice; 8-32 typical) 20// 4. wrap in double quotes 21// 22// Comparison follows RFC 7232 §2.3.2: strong match requires both 23// to be strong and byte-identical; weak match matches regardless 24// of W/- prefix. 25// 26// Invariants: 27// ET1 Output always ends with a trailing DQUOTE. 28// ET2 Weak prefix is exactly "W/" (2 ASCII bytes, case-sensitive). 29// ET3 Caller chooses truncation length; 8 hex chars (4 bytes 30// of entropy) is the minimum practical, 32 (128 bits) is 31// plenty. 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_sha256.nx" 41import "nx_hex_codec.nx" 42 43const ETAG_ERR_SHORT: i64 = -1 44 45// Generate a strong ETag from data[0..n] into out buffer. 46// `trunc` is how many hex characters to include (without quotes). 47// Writes: "<trunc hex chars>" (trunc + 2 bytes total, DQUOTE-wrapped). 48// Returns bytes written or ETAG_ERR_SHORT. 49func etag_gen_strong(data: *u8, n: i64, 50 out: *u8, cap: i64, trunc: i64) -> i64 { 51 if trunc < 2 { return ETAG_ERR_SHORT } 52 if trunc > 64 { return ETAG_ERR_SHORT } 53 let total: i64 = trunc + 2 54 if cap < total { return ETAG_ERR_SHORT } 55 56 let digest: *u8 = sys_mmap(64) 57 sha256_digest(data, n, digest) 58 let hex_out: *u8 = sys_mmap(96) 59 hex_encode(digest, 32, hex_out) 60 61 out[0] = 0x22 // '\"' 62 var i: i64 = 0 63 while i < trunc { 64 out[1 + i] = hex_out[i] 65 i = i + 1 66 } 67 out[1 + trunc] = 0x22 68 return total 69} 70 71// Generate a weak ETag: W/"<trunc hex>". 72func etag_gen_weak(data: *u8, n: i64, 73 out: *u8, cap: i64, trunc: i64) -> i64 { 74 if cap < trunc + 4 { return ETAG_ERR_SHORT } 75 out[0] = 0x57 // 'W' 76 out[1] = 0x2F // '/' 77 let inner: i64 = etag_gen_strong(data, n, out + 2, cap - 2, trunc) 78 if inner < 0 { return inner } 79 return inner + 2 80} 81 82// Test whether an ETag value begins with the weak "W/" prefix. 83func etag_is_weak(tag: *u8, n: i64) -> i64 { 84 if n < 3 { return 0 } 85 if tag[0] != 0x57 { return 0 } 86 if tag[1] != 0x2F { return 0 } 87 return 1 88} 89 90// Strip outer quotes. Writes (start_off, end_off) into the 91// caller's slot pair. Returns 1 if the tag was well-formed, 92// 0 otherwise. 93func etag_strip_quotes(tag: *u8, n: i64, 94 start_out: *i64, end_out: *i64) -> i64 { 95 var start: i64 = 0 96 if etag_is_weak(tag, n) == 1 { start = 2 } 97 if n < start + 2 { return 0 } 98 if tag[start] != 0x22 { return 0 } // opening quote 99 if tag[n - 1] != 0x22 { return 0 } // closing quote 100 *start_out = start + 1 101 *end_out = n - 1 102 return 1 103} 104 105// Weak match: ignore W/- prefix, compare inner opaque bytes. 106// Returns 1 if they match, 0 otherwise. 107func etag_weak_match(a: *u8, a_len: i64, 108 b: *u8, b_len: i64) -> i64 { 109 let a_start: *i64 = (sys_mmap(32)) as *i64 110 let a_end: *i64 = (sys_mmap(32)) as *i64 111 let b_start: *i64 = (sys_mmap(32)) as *i64 112 let b_end: *i64 = (sys_mmap(32)) as *i64 113 if etag_strip_quotes(a, a_len, a_start, a_end) != 1 { return 0 } 114 if etag_strip_quotes(b, b_len, b_start, b_end) != 1 { return 0 } 115 let la: i64 = *a_end - *a_start 116 let lb: i64 = *b_end - *b_start 117 if la != lb { return 0 } 118 var i: i64 = 0 119 while i < la { 120 if a[*a_start + i] != b[*b_start + i] { return 0 } 121 i = i + 1 122 } 123 return 1 124} 125 126// Strong match: both must be strong AND byte-identical inner. 127func etag_strong_match(a: *u8, a_len: i64, 128 b: *u8, b_len: i64) -> i64 { 129 if etag_is_weak(a, a_len) == 1 { return 0 } 130 if etag_is_weak(b, b_len) == 1 { return 0 } 131 return etag_weak_match(a, a_len, b, b_len) 132} 133 134// Compile-only smoke. 135func main() -> i64 { 136 let tag1: *u8 = sys_mmap(64) 137 let tag2: *u8 = sys_mmap(64) 138 let tag3: *u8 = sys_mmap(64) 139 140 let data: *u8 = "hello world" 141 let l1: i64 = etag_gen_strong(data, 11, tag1, 64, 16) 142 if l1 != 18 { return 1 } // 16 hex + 2 quotes 143 if tag1[0] != 0x22 { return 2 } 144 if tag1[17] != 0x22 { return 3 } 145 146 let l2: i64 = etag_gen_weak(data, 11, tag2, 64, 16) 147 if l2 != 20 { return 4 } // W/ + 18 = 20 148 if tag2[0] != 0x57 { return 5 } 149 if tag2[1] != 0x2F { return 6 } 150 151 // Same underlying data -> weak-match equal. 152 if etag_weak_match(tag1, l1, tag2, l2) != 1 { return 7 } 153 // Strong match fails because tag2 is weak. 154 if etag_strong_match(tag1, l1, tag2, l2) != 0 { return 8 } 155 156 // Different data -> no match. 157 let l3: i64 = etag_gen_strong("hello worlds", 12, tag3, 64, 16) 158 if etag_weak_match(tag1, l1, tag3, l3) != 0 { return 9 } 159 if etag_strong_match(tag1, l1, tag3, l3) != 0 { return 10 } 160 return 0 161}