code wiki / _hdl_build / nx_media_crypt_gate.nx
nx_media_crypt_gate.nx source
↩ module page · 126 lines · 6890 B
1import "nx_gate_base.nx"
2// nx_media_crypt_gate.nx -- ChaCha20 (RFC 8439) KAT + E2EE property gate. license_tier: ORIGINAL
3import "nx_syscalls.nx"
4import "nx_media_crypt.nx"
5
6func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
7" as *u8); return ok }
8func gn(v: i64) -> i64 {
9 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m}
10 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}
11 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
12func ck(name: *u8, ok: i64, p: *i64, t: *i64) -> i64 {
13 t[0]=t[0]+1; gw(" " as *u8); gw(name)
14 if ok==1 { gw(" PASS\n" as *u8); p[0]=p[0]+1 } else { gw(" FAIL\n" as *u8) }
15 return 0 }
16func fill(b: *u8, n: i64, seed: i64) -> i64 { var i: i64=0; while i<n { b[i]=((i*seed+7)&255) as u8; i=i+1 } return 0 }
17func same(a: *u8, b: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if a[i]!=b[i]{return 0} i=i+1 } return 1 }
18func popdiff(a: *u8, b: *u8, n: i64) -> i64 { var d: i64=0; var i: i64=0
19 while i<n { var x: i64=(a[i]^b[i])&255; while x>0 { d=d+(x&1); x=x/2 } i=i+1 } return d }
20func hexdec(hex: *u8, nbytes: i64, out: *u8) -> i64 { // decode nbytes from a 2*nbytes hex string
21 var i: i64=0
22 while i<nbytes { out[i]=((mc_hexval(hex[i*2]&0xff)<<4)|mc_hexval(hex[i*2+1]&0xff)) as u8; i=i+1 }
23 return 0 }
24
25func main() -> i64 {
26 gw("=== nx_media_crypt_gate: ChaCha20 (RFC 8439) + E2EE properties ===\n" as *u8)
27 let p: *i64 = sys_mmap(16) as *i64; let t: *i64 = sys_mmap(16) as *i64
28 let scr: *u8 = sys_mmap(512) // ChaCha scratch (>= MC_SCRATCH), passed to every mc_xform
29
30 // ---- T0 KAT: RFC 8439 section 2.3.2 block function (key 00..1f, nonce 00000009..4a, counter 1) ----
31 let key: *u8 = sys_mmap(64)
32 var ki: i64=0; while ki<32 { key[ki]=ki as u8; ki=ki+1 } // key bytes 00..1f
33 let non: *u8 = sys_mmap(16)
34 non[0]=0 as u8; non[1]=0 as u8; non[2]=0 as u8; non[3]=9 as u8
35 non[4]=0 as u8; non[5]=0 as u8; non[6]=0 as u8; non[7]=0x4a as u8
36 non[8]=0 as u8; non[9]=0 as u8; non[10]=0 as u8; non[11]=0 as u8
37 let s: *i64 = sys_mmap(16*8) as *i64; let w: *i64 = sys_mmap(16*8) as *i64
38 let blk: *u8 = sys_mmap(64)
39 mc_block(key, non, 1, blk, s, w)
40 let exp: *u8 = sys_mmap(64)
41 hexdec("10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e" as *u8, 64, exp)
42 ck("T0 KAT RFC8439 2.3.2 block function (64 bytes) \x00" as *u8, same(blk, exp, 64), p, t)
43
44 // ---- T0b KAT: RFC 8439 section 2.4.2 full encryption (counter starts at 1, sunscreen plaintext) ----
45 let non2: *u8 = sys_mmap(16)
46 var ni: i64=0; while ni<12 { non2[ni]=0 as u8; ni=ni+1 }
47 non2[7]=0x4a as u8 // nonce 00 00 00 00 00 00 00 4a 00 00 00 00
48 let pt: *u8 = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." as *u8
49 var pl: i64=0; while pt[pl]!=(0 as u8){pl=pl+1} // 114 bytes
50 let ct: *u8 = sys_mmap(256)
51 var ci: i64=0; while ci<pl { ct[ci]=pt[ci]; ci=ci+1 }
52 mc_xform(key, non2, 1, ct, pl, scr) // ctr0=1 per the vector
53 let cexp: *u8 = sys_mmap(256)
54 hexdec("6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d" as *u8, 114, cexp)
55 ck("T0b KAT RFC8439 2.4.2 full encryption (114 bytes)\x00" as *u8, same(ct, cexp, pl), p, t)
56
57 // ---- E2EE properties (via the mc_ media API) ----
58 let p2: *i64 = sys_mmap(16) as *i64
59 let ek: *u8 = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" as *u8
60 let ek2: *u8 = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1e" as *u8 // last byte 1f->1e
61 let key1: *u8 = sys_mmap(64); let keyB: *u8 = sys_mmap(64)
62 mc_key(ek, 64, key1); mc_key(ek2, 64, keyB)
63 let n1: *u8 = sys_mmap(16); mc_nonce("SENDER01" as *u8, 42, n1)
64
65 let L: i64 = 200
66 let dpt: *u8 = sys_mmap(256); fill(dpt, L, 3)
67 let c1: *u8 = sys_mmap(256); let d1: *u8 = sys_mmap(256)
68
69 // T1 round-trip + length-preserving
70 var i: i64=0; while i<L { c1[i]=dpt[i]; i=i+1 }
71 mc_xform(key1, n1, 0, c1, L, scr)
72 var ok: i64=1
73 if same(c1, dpt, L)==1 { ok=0 }
74 i=0; while i<L { d1[i]=c1[i]; i=i+1 }
75 mc_xform(key1, n1, 0, d1, L, scr)
76 if same(d1, dpt, L)!=1 { ok=0 }
77 ck("T1 round-trip identity + length-preserving \x00" as *u8, ok, p, t)
78
79 // T2 per-frame nonce -> different ciphertext (no keystream reuse across the SFU seq-rewrite)
80 ok=1
81 let n2: *u8 = sys_mmap(16); mc_nonce("SENDER01" as *u8, 43, n2)
82 let c2: *u8 = sys_mmap(256); i=0; while i<L { c2[i]=dpt[i]; i=i+1 }
83 mc_xform(key1, n2, 0, c2, L, scr)
84 if same(c1, c2, L)==1 { ok=0 }
85 ck("T2 per-frame nonce -> unique keystream \x00" as *u8, ok, p, t)
86
87 // T3 per-sender nonce -> different keystream
88 ok=1
89 let n3: *u8 = sys_mmap(16); mc_nonce("SENDER02" as *u8, 42, n3)
90 let c3: *u8 = sys_mmap(256); i=0; while i<L { c3[i]=dpt[i]; i=i+1 }
91 mc_xform(key1, n3, 0, c3, L, scr)
92 if same(c1, c3, L)==1 { ok=0 }
93 ck("T3 per-sender nonce -> unique keystream \x00" as *u8, ok, p, t)
94
95 // T4 wrong key -> garbage (relay-blind: token holder without ekey cannot recover)
96 ok=1
97 let bad: *u8 = sys_mmap(256); i=0; while i<L { bad[i]=c1[i]; i=i+1 }
98 mc_xform(keyB, n1, 0, bad, L, scr)
99 if same(bad, dpt, L)==1 { ok=0 }
100 ck("T4 wrong key -> garbage (relay-blind) \x00" as *u8, ok, p, t)
101
102 // T5 keystream avalanche ~50% on a 1-bit key flip
103 ok=1
104 let z1: *u8 = sys_mmap(256); let z2: *u8 = sys_mmap(256)
105 i=0; while i<L { z1[i]=0 as u8; z2[i]=0 as u8; i=i+1 }
106 mc_xform(key1, n1, 0, z1, L, scr)
107 mc_xform(keyB, n1, 0, z2, L, scr)
108 let bits: i64 = popdiff(z1, z2, L)
109 if bits < (L*8*35)/100 { ok=0 }
110 if bits > (L*8*65)/100 { ok=0 }
111 ck("T5 keystream avalanche ~50% on key flip \x00" as *u8, ok, p, t)
112
113 // T6 non-block-aligned tail (200 % 64 = 8) round-trips
114 ok=1
115 let tl: *u8 = sys_mmap(64); fill(tl, 8, 9)
116 let tc: *u8 = sys_mmap(64); i=0; while i<8 { tc[i]=tl[i]; i=i+1 }
117 mc_xform(key1, n1, 0, tc, 8, scr)
118 if same(tc, tl, 8)==1 { ok=0 }
119 mc_xform(key1, n1, 0, tc, 8, scr)
120 if same(tc, tl, 8)!=1 { ok=0 }
121 ck("T6 non-aligned tail round-trip \x00" as *u8, ok, p, t)
122
123 gw("MEDIA-CRYPT: " as *u8); gn(p[0]); gw("/" as *u8); gn(t[0])
124 if p[0]==t[0] { gw(" ALL GREEN (ChaCha20 KAT-verified)\n" as *u8); return 0 }
125 gw(" RED\n" as *u8)
126 return 1 }