code wiki / _hdl_build / nx_vault_transit.nx
nx_vault_transit.nx source
↩ module page · 60 lines · 3338 B
1// nx_vault_transit.nx -- sovereign TRANSIT engine (encryption-as-a-service): the HashiCorp Vault "transit"
2// gap from vault_capability_census.tsv. The vault holds the KEY; the caller's data passes THROUGH and is
3// NEVER stored. Versioned keyring: encrypt stamps the LATEST key version into a self-describing envelope;
4// decrypt ROUTES to the version named in the envelope (so old ciphertext still opens after key rotation);
5// rewrap re-encrypts an old-version envelope to the latest version WITHOUT returning plaintext to the caller
6// (rotate keys without re-exposing the data). REAL AES-128-GCM (composes nx_aes128_gcm = NIST SP800-38D),
7// fails-closed on tamper. Production keys are vault-sealed (nx_vault + nx_machine_key); here the keyring is
8// caller-supplied. license_tier: ORIGINAL
9import "nx_aes128_gcm.nx"
10import "nx_syscalls.nx"
11const TR_MAGIC_4096: i64 = 4096
12
13// envelope: [ver:1][iv:12][tag:16][ciphertext:ptlen]
14const TR_VER_OFF: i64 = 0
15const TR_IV_OFF: i64 = 1
16const TR_TAG_OFF: i64 = 13
17const TR_CT_OFF: i64 = 29
18
19// pointer to the 16-byte key for a 1-based version inside a contiguous keyring.
20func tr_keyptr(keyring: *u8, ver: i64) -> *u8 { return ((keyring as i64) + (ver - 1) * 16) as *u8 }
21
22// encrypt pt under the LATEST key version; write the self-describing envelope. returns envelope length.
23func tr_encrypt(keyring: *u8, latest_ver: i64, iv12: *u8, pt: *u8, pt_len: i64, env_out: *u8) -> i64 {
24 env_out[TR_VER_OFF] = latest_ver as u8
25 var i: i64 = 0
26 while i < 12 { env_out[TR_IV_OFF + i] = iv12[i]; i = i + 1 }
27 let z: *u8 = sys_mmap(16) // mmap is zero-filled -> empty AAD
28 let tag: *u8 = ((env_out as i64) + TR_TAG_OFF) as *u8
29 let ct: *u8 = ((env_out as i64) + TR_CT_OFF) as *u8
30 nx_aes128_gcm_seal(tr_keyptr(keyring, latest_ver), iv12, z, 0, pt, pt_len, ct, tag)
31 return TR_CT_OFF + pt_len
32}
33
34// decrypt: route to the envelope's key version, AES-GCM open. returns pt_len, or -1 (fails-closed).
35func tr_decrypt(keyring: *u8, key_count: i64, env: *u8, env_len: i64, pt_out: *u8) -> i64 {
36 let ver: i64 = env[TR_VER_OFF] as i64
37 if ver < 1 { return 0 - 1 }
38 if ver > key_count { return 0 - 1 }
39 let iv: *u8 = ((env as i64) + TR_IV_OFF) as *u8
40 let tag: *u8 = ((env as i64) + TR_TAG_OFF) as *u8
41 let ct: *u8 = ((env as i64) + TR_CT_OFF) as *u8
42 let ct_len: i64 = env_len - TR_CT_OFF
43 let z: *u8 = sys_mmap(16)
44 let rc: i64 = nx_aes128_gcm_open(tr_keyptr(keyring, ver), iv, z, 0, ct, ct_len, tag, pt_out)
45 if rc != 0 { return 0 - 1 }
46 return ct_len
47}
48
49// rewrap: re-encrypt an old-version envelope to the LATEST version. plaintext is decrypted INTERNALLY then
50// ZEROED; the caller receives ONLY the new ciphertext envelope (the transit rotation guarantee). returns
51// new envelope length, or -1.
52func tr_rewrap(keyring: *u8, key_count: i64, latest_ver: i64, env_in: *u8, env_in_len: i64, new_iv12: *u8, env_out: *u8) -> i64 {
53 let scratch: *u8 = sys_mmap(TR_MAGIC_4096)
54 let n: i64 = tr_decrypt(keyring, key_count, env_in, env_in_len, scratch)
55 if n < 0 { return 0 - 1 }
56 let out_len: i64 = tr_encrypt(keyring, latest_ver, new_iv12, scratch, n, env_out)
57 var i: i64 = 0
58 while i < n { scratch[i] = 0; i = i + 1 } // wipe plaintext; caller never sees it
59 return out_len
60}