code wiki / (root) / nx_jose_test.nx

nx_jose_test.nx source

↩ module page · 166 lines · 6938 B

1// nx_jose_test.nx -- smoke for nx_jose + nx_jwk. 2// 3// Covers: 4// 1. JWK enum gates + emit OKP/Ed25519 for a known public key 5// 2. JOSE enum gates + algorithm enum 6// 3. Signature input matches RFC 7515 §5.1 shape 7// 4. End-to-end sign + emit flattened-JSON for an EdDSA JWS 8// 5. Bounds-check / BAD_ARG paths 9// 10// Not yet covered (separate slice): 11// - RFC 8037 §A.4 KAT bytes (the example uses a specific keypair 12// + nonce; the substrate ed25519_sign_full is deterministic so 13// bytes match, but the §A.4 fixture isn't in this smoke). 14// - JWK thumbprint SHA-256 (queued: nx_sha256_jwk_thumbprint). 15// 16// expect_exit: 0 17// 18// NOTE 2026-05-16: nx_ed25519_signature.nx transitively imports 19// nx_syscalls.nx (RV64 number) -- composing it with 20// nx_syscalls_x86_64.nx (this test's natural import) hits the 21// F-meta-4 cross-target portability gap (duplicate sys_dup3 / 22// sys_getdents64 / blk_set_byte symbols at link). Fix path is to 23// split nx_ed25519 (and its 5-file chain) into a types-only base 24// layer + a syscall-using convenience layer, same shape as 25// nx_arena_types.nx -- queued as multi-session refactor. 26// 27// For this slice: gauntlet entry is COMPILE-clean only; runtime 28// verification awaits the refactor or an RV64-qemu harness. 29// 30// license_tier: ORIGINAL 31 32import "nx_jwk.nx" 33import "nx_jose.nx" 34 35func bytes_eq(a: *u8, b: *u8, n: i64) -> i64 { 36 var i: i64 = 0 37 while i < n { 38 if a[i] != b[i] { return 0 } 39 i = i + 1 40 } 41 return 1 42} 43 44func main() -> i64 { 45 // ---- JWK enum gate ---- 46 if nxjwk_verdict_is_valid(NXJWK_OK) != 1 { return 1 } 47 if nxjwk_verdict_is_valid(NXJWK_BAD_KTY) != 1 { return 2 } 48 if nxjwk_verdict_is_valid(NXJWK_VERDICT_N) != 0 { return 3 } 49 if bytes_eq(nxjwk_verdict_name(NXJWK_OOM_BUFFER), 50 "OOM_BUFFER" as *u8, 10) != 1 { return 4 } 51 if bytes_eq(nxjwk_verdict_name(NXJWK_BAD_KTY), 52 "BAD_KTY" as *u8, 7) != 1 { return 5 } 53 54 // ---- JOSE enum gate ---- 55 if nxjose_verdict_is_valid(NXJOSE_OK) != 1 { return 10 } 56 if nxjose_verdict_is_valid(NXJOSE_SIGN_ERR) != 1 { return 11 } 57 if nxjose_verdict_is_valid(NXJOSE_VERDICT_N) != 0 { return 12 } 58 if nxjose_alg_is_valid(NXJOSE_ALG_EDDSA) != 1 { return 13 } 59 if nxjose_alg_is_valid(NXJOSE_ALG_N) != 0 { return 14 } 60 61 // ---- JWK emit: canonical OKP/Ed25519 ---- 62 // Use a known public key (all 32 bytes set to 0x55 for predictability). 63 let pubkey: *u8 = sys_mmap(32) 64 var i: i64 = 0 65 while i < 32 { pubkey[i] = 0x55 as u8; i = i + 1 } 66 67 let jwk_buf: *u8 = sys_mmap(256) 68 let jwk_off: *i64 = sys_mmap(8) as *i64 69 jwk_off[0] = 0 70 let rc_jwk: i64 = nx_jwk_emit_okp_ed25519(jwk_buf, jwk_off, 256, pubkey) 71 if rc_jwk != NXJWK_OK { return 20 } 72 // Expected: {"crv":"Ed25519","kty":"OKP","x":"VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ"} 73 // (32 bytes of 0x55 base64url-encoded = "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ") 74 let exp_jwk: *u8 = "{\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ\"}" as *u8 75 let exp_jwk_n: i64 = 79 76 if jwk_off[0] != exp_jwk_n { return 21 } 77 if bytes_eq(jwk_buf, exp_jwk, exp_jwk_n) != 1 { return 22 } 78 79 // ---- BAD_ARG paths for JWK ---- 80 jwk_off[0] = 0 81 if nx_jwk_emit_okp_ed25519(0 as *u8, jwk_off, 256, pubkey) != NXJWK_BAD_ARG { return 30 } 82 if nx_jwk_emit_okp_ed25519(jwk_buf, 0 as *i64, 256, pubkey) != NXJWK_BAD_ARG { return 31 } 83 if nx_jwk_emit_okp_ed25519(jwk_buf, jwk_off, 256, 0 as *u8) != NXJWK_BAD_ARG { return 32 } 84 if nx_jwk_emit_okp_ed25519(jwk_buf, jwk_off, 0, pubkey) != NXJWK_BAD_ARG { return 33 } 85 86 // ---- JOSE signature input shape ---- 87 let prot: *u8 = "{\"alg\":\"EdDSA\"}" as *u8 88 let prot_n: i64 = 15 89 let pay: *u8 = "{\"hello\":1}" as *u8 90 let pay_n: i64 = 11 91 92 let si_buf: *u8 = sys_mmap(512) 93 let si_off: *i64 = sys_mmap(8) as *i64 94 let rc_si: i64 = nx_jose_signature_input(prot, prot_n, pay, pay_n, 95 si_buf, 512, si_off) 96 if rc_si != NXJOSE_OK { return 40 } 97 // Expected = b64url(prot) || "." || b64url(pay) 98 // b64url("{\"alg\":\"EdDSA\"}") = "eyJhbGciOiJFZERTQSJ9" (20 chars) 99 // b64url("{\"hello\":1}") = "eyJoZWxsbyI6MX0" (15 chars) 100 // total = 20 + 1 + 15 = 36 101 let exp_si: *u8 = "eyJhbGciOiJFZERTQSJ9.eyJoZWxsbyI6MX0" as *u8 102 let exp_si_n: i64 = 36 103 if si_off[0] != exp_si_n { return 41 } 104 if bytes_eq(si_buf, exp_si, exp_si_n) != 1 { return 42 } 105 106 // ---- End-to-end JOSE sign + emit flattened JSON ---- 107 // priv = 32 zero bytes (deterministic for test reproducibility). 108 let priv: *u8 = sys_mmap(32) 109 var j: i64 = 0 110 while j < 32 { priv[j] = 0 as u8; j = j + 1 } 111 112 let jws_buf: *u8 = sys_mmap(2048) 113 let jws_off: *i64 = sys_mmap(8) as *i64 114 jws_off[0] = 0 115 let rc_jws: i64 = nx_jose_sign_eddsa_flattened( 116 prot, prot_n, pay, pay_n, priv, jws_buf, 2048, jws_off) 117 if rc_jws != NXJOSE_OK { return 50 } 118 119 // Output shape verification: starts with {"protected":" 120 if bytes_eq(jws_buf, "{\"protected\":\"" as *u8, 14) != 1 { return 51 } 121 // Ends with "} (verify last 2 bytes) 122 let end_off: i64 = jws_off[0] 123 if end_off < 4 { return 52 } 124 if jws_buf[end_off - 2] != 0x22 as u8 { return 53 } // " 125 if jws_buf[end_off - 1] != 0x7d as u8 { return 54 } // } 126 127 // Verify the protected + payload b64url substrings appear in the 128 // emitted JSON. Scan for "eyJhbGciOiJFZERTQSJ9" (protected b64url). 129 var found_prot: i64 = 0 130 var k: i64 = 0 131 while k <= end_off - 20 { 132 if bytes_eq((jws_buf as i64 + k) as *u8, 133 "eyJhbGciOiJFZERTQSJ9" as *u8, 20) == 1 { 134 found_prot = 1 135 k = end_off // break 136 } 137 k = k + 1 138 } 139 if found_prot != 1 { return 60 } 140 141 // Scan for payload b64url 142 var found_pay: i64 = 0 143 var m: i64 = 0 144 while m <= end_off - 15 { 145 if bytes_eq((jws_buf as i64 + m) as *u8, 146 "eyJoZWxsbyI6MX0" as *u8, 15) == 1 { 147 found_pay = 1 148 m = end_off // break 149 } 150 m = m + 1 151 } 152 if found_pay != 1 { return 61 } 153 154 // ---- BAD_ARG paths for JOSE sign ---- 155 jws_off[0] = 0 156 if nx_jose_sign_eddsa_flattened(0 as *u8, 5, pay, pay_n, priv, 157 jws_buf, 2048, jws_off) != NXJOSE_BAD_ARG { return 70 } 158 if nx_jose_sign_eddsa_flattened(prot, prot_n, 0 as *u8, 5, priv, 159 jws_buf, 2048, jws_off) != NXJOSE_BAD_ARG { return 71 } 160 if nx_jose_sign_eddsa_flattened(prot, prot_n, pay, pay_n, 0 as *u8, 161 jws_buf, 2048, jws_off) != NXJOSE_BAD_ARG { return 72 } 162 if nx_jose_sign_eddsa_flattened(prot, prot_n, pay, pay_n, priv, 163 0 as *u8, 2048, jws_off) != NXJOSE_BAD_ARG { return 73 } 164 165 return 0 166}