code wiki / _hdl_build / nx_hr_pow.nx

nx_hr_pow.nx source

↩ module page · 76 lines · 5353 B

1// nx_hr_pow.nx -- NISHI HR, the PROOF-OF-WORK challenge (the "cost" limb of behavioral bot defense). Privacy- 2// preserving: NO IP/PII -- a visitor just has to PAY a small compute cost the server can cheaply verify, which is 3// negligible for one human but ruinous for a bot hammering many requests. STATELESS + tamper-proof: the challenge 4// is HMAC-bound to a server secret (so the server verifies it issued it without storing it) and carries an EXPIRY 5// (so it can't be replayed forever). Sovereign crypto: composes sha256_digest. The client must find a `solution` 6// integer s.t. sha256("<nonce>/<solution>") has >= `difficulty` leading zero BITS. 7// challenge wire form: "<nonce>:<difficulty>:<expiry>:<mac-hex>" (mac = sha256(secret || "<nonce>:<diff>:<exp>")) 8// license_tier: ORIGINAL 9import "nx_sha256.nx" // sha256_digest(bytes,n,out32) 10import "nx_syscalls.nx" 11 12func pow_puts(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[o+i]=s[i]; i=i+1 } return o+i } 13func pow_putn(dst: *u8, o: i64, v: i64) -> i64 { if v==0 { dst[o]=48 as u8; return o+1 } var m: i64=v; if m<0{m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var j:i64=0; while j<k{dst[o+j]=t[k-1-j];j=j+1} return o+k } 14func pow_hex(inp: *u8, n: i64, out: *u8) -> i64 { let hx: *u8="0123456789abcdef" as *u8; var i: i64=0; while i<n { out[i*2]=hx[((inp[i] as i64)>>4)&15]; out[i*2+1]=hx[(inp[i] as i64)&15]; i=i+1 } return n*2 } 15func pow_atoi(buf: *u8, s: i64, e: i64) -> i64 { var v: i64=0; var i: i64=s; while i<e { let c: i64=buf[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48); i=i+1 } else {i=e} } else {i=e} } return v } 16func pow_colon(buf: *u8, start: i64, n: i64) -> i64 { var i: i64=start; while i<n { if buf[i]==(58 as u8) { return i } i=i+1 } return n } 17func pow_eqrange(a: *u8, as0: i64, ae: i64, b: *u8, bs: i64, be: i64) -> i64 { if (ae-as0)!=(be-bs) { return 0 } var k: i64=0; while k<(ae-as0) { if a[as0+k]!=b[bs+k] { return 0 } k=k+1 } return 1 } 18 19// count leading zero BITS of the 32-byte hash. 20func pow_lead_zero_bits(h: *u8) -> i64 { 21 var bits: i64 = 0; var i: i64 = 0 22 while i < 32 { 23 let b: i64 = h[i] as i64 24 if b == 0 { bits = bits + 8; i = i + 1 } else { 25 var mask: i64 = 128; var c: i64 = 0; var go: i64 = 1 26 while go == 1 { if mask <= 0 { go = 0 } else { if (b & mask) == 0 { c = c + 1; mask = mask / 2 } else { go = 0 } } } 27 bits = bits + c; i = 32 28 } 29 } 30 return bits 31} 32// the MAC over a payload: hex(sha256(secret || payload)). writes to out_hex (64 chars), returns len. 33func pow_mac(secret: *u8, slen: i64, payload: *u8, plen: i64, out_hex: *u8) -> i64 { 34 let buf: *u8 = sys_mmap(slen + plen + 16) 35 var i: i64 = 0; while i < slen { buf[i] = secret[i]; i = i + 1 } 36 var j: i64 = 0; while j < plen { buf[slen+j] = payload[j]; j = j + 1 } 37 let h: *u8 = sys_mmap(32); sha256_digest(buf, slen + plen, h) 38 return pow_hex(h, 32, out_hex) 39} 40// does sha256("<nonce>/<solution>") have >= difficulty leading zero bits? 41func pow_solution_ok(nonce: *u8, noncelen: i64, solution: i64, difficulty: i64) -> i64 { 42 let buf: *u8 = sys_mmap(noncelen + 40) 43 var i: i64 = 0; while i < noncelen { buf[i] = nonce[i]; i = i + 1 } 44 var o: i64 = noncelen; buf[o] = 47 as u8; o = o + 1 // '/' 45 o = pow_putn(buf, o, solution) 46 let h: *u8 = sys_mmap(32); sha256_digest(buf, o, h) 47 if pow_lead_zero_bits(h) >= difficulty { return 1 } 48 return 0 49} 50// ISSUE a challenge "<nonce>:<diff>:<exp>:<mac>" into out (NUL-term); returns len. 51func pow_make(secret: *u8, slen: i64, nonce: i64, difficulty: i64, expiry_ts: i64, out: *u8) -> i64 { 52 var o: i64 = pow_putn(out, 0, nonce); out[o]=58 as u8; o=o+1 53 o = pow_putn(out, o, difficulty); out[o]=58 as u8; o=o+1 54 o = pow_putn(out, o, expiry_ts) 55 let mac: *u8 = sys_mmap(80); pow_mac(secret, slen, out, o, mac) // mac over "<nonce>:<diff>:<exp>" 56 out[o]=58 as u8; o=o+1 57 var k: i64=0; while mac[k]!=(0 as u8) { if k<64 { out[o]=mac[k]; o=o+1; k=k+1 } else { k=64 } } if k==0 { var z: i64=0; while z<64 { out[o]=mac[z]; o=o+1; z=z+1 } } 58 out[o]=0 as u8; return o 59} 60// VERIFY: challenge well-formed + OUR mac (not forged) + not expired + solution meets difficulty. 1=pass, 0=reject. 61func pow_verify(chal: *u8, chlen: i64, solution: i64, secret: *u8, slen: i64, now_ts: i64) -> i64 { 62 let c1: i64 = pow_colon(chal, 0, chlen) // end of nonce 63 if c1 >= chlen { return 0 } 64 let c2: i64 = pow_colon(chal, c1+1, chlen) // end of difficulty 65 if c2 >= chlen { return 0 } 66 let c3: i64 = pow_colon(chal, c2+1, chlen) // end of expiry; mac follows 67 if c3 >= chlen { return 0 } 68 let difficulty: i64 = pow_atoi(chal, c1+1, c2) 69 let expiry: i64 = pow_atoi(chal, c2+1, c3) 70 // recompute the mac over payload = chal[0..c3) and compare to chal[c3+1..chlen) 71 let mymac: *u8 = sys_mmap(80); pow_mac(secret, slen, chal, c3, mymac) 72 var mymaclen: i64 = 0; while mymac[mymaclen]!=(0 as u8) { mymaclen = mymaclen + 1 } 73 if pow_eqrange(mymac, 0, mymaclen, chal, c3+1, chlen) == 0 { return 0 } // forged / tampered 74 if now_ts > expiry { return 0 } // expired 75 return pow_solution_ok(chal, c1, solution, difficulty) // nonce = chal[0..c1) 76}