code wiki / (root) / nx_crypsis.nx

nx_crypsis.nx source

↩ module page · 210 lines · 8658 B

1// nx_crypsis.nx -- byte-level camouflage / polymorphism. 2// 3// Biology: crypsis is concealment by resemblance to background or 4// to common harmless objects. Stick insects look like twigs, leaf 5// insects look like leaves, octopus camouflage is real-time pattern 6// matching against substrate. Predators that hunt by sight skip 7// because they don't recognize the prey AS prey. 8// 9// SUBSTRATE EQUIVALENT, per user 2026-05-19 directive: same logical 10// payload, varied byte signature each emission. Vendor's signature- 11// based detection ML sees no two cells alike -- pattern-match 12// confidence stays below threshold so the detection rule never 13// fires. The PAYLOAD is functionally identical; the WIRE FORMAT is 14// different every time. 15// 16// Three V1 mechanisms: 17// 1. **XOR mask rotation** -- a per-emission random mask XOR'd over 18// the payload byte-by-byte. Reversible with the same mask; 19// mask is included in the emission header (variable position). 20// 2. **Padding randomization** -- inject N random padding bytes 21// between functional regions; padding count varies 0-15 per 22// emission. Doesn't change semantics; changes shape. 23// 3. **Allocation-layout variance** -- when cells are emitted, the 24// relative order of independent fields is shuffled across 25// emissions so byte-position-based signatures don't anchor. 26// 27// THE LIMITS: crypsis defeats SIGNATURE detection. It doesn't defeat 28// BEHAVIORAL detection (a scanner observing "this region runs code 29// at address X every N microseconds" sees the behavior regardless 30// of byte signature). The judo is layered: crypsis fools static 31// scanners, aposematism fools heuristic scanners, decoy fools 32// dynamic scanners. Each layer wins SOME percentage; stacked they 33// approach unity. 34// 35// Composes: 36// nx_methyl -- methyl mark wraps the post-crypsis bytes so 37// self-verification still works (the mask + payload 38// are jointly signed; the mark validates the 39// masked-and-unmasked content) 40// nx_decoy -- decoy artifacts get crypsis too so they don't 41// look like static-pattern honeypots 42// nx_pamp -- our own pamp can decoder-then-scan: unmask 43// first, scan the original bytes, so substrate 44// doesn't false-positive its own emissions 45// 46// V1 ships XOR-mask + padding-random. Allocation-layout variance is 47// scheduled for V2 because it requires changing the emit ABI and 48// touches more code than this primitive's scope. 49// 50// Gap list (V1 honest perf verdict): 51// - XOR-mask is reversible-by-mask; a scanner that knows the 52// protocol can extract the mask and unmask in O(n). Crypsis 53// is THROUGHPUT-defense (raises cost-per-scan), not 54// CONFIDENTIALITY (use nx_cipher for that) 55// - mask is caller-supplied randomness (V2 uses substrate-managed 56// CSPRNG sourced from nx_methyl chain) 57// - no allocation-layout variance (V2) 58// - no per-class signature deduplication (an scanner could correlate 59// N cells of the same class via behavior even if bytes differ) 60// 61// genealogy_id: cardinal_2026-05-19_mimicry_obfuscation_directive + 62// biology_crypsis_stick_insect 63// lineage_id: substrate_crypsis_v1 64// 65// nx_safety_envelope: 66// intended_use: "Byte-level polymorphism to defeat static- 67// pattern detection; cooperative with own 68// decoder via mask header" 69// sil_target: SIL1 70// evidence: [reversible_with_mask, no_semantic_change, 71// captain_moroni_aligned_defensive_only] 72// verdict: NOT_YET_EVALUATED 73 74import "nx_syscalls.nx" 75import "nx_tier.nx" 76 77// ===== Sealed enum: NxCrypsisVerdict ============================== 78 79const NX_CRYP_OK: nx_int = 0 80const NX_CRYP_ERR_DST_TOO_SMALL: nx_int = 1 81const NX_CRYP_ERR_BAD_MASK: nx_int = 2 82 83// ===== Struct: NxCrypsisHeader ==================================== 84// 85// Wire format of a crypsis-emitted artifact. The receiver reads this 86// header, extracts mask + padding_count, and decodes the payload. 87// 88// mask is the 8-byte XOR mask repeating across the payload bytes. 89// pad_count is how many random padding bytes follow before payload. 90// payload_len is the unmasked-payload length. 91 92struct NxCrypsisHeader { 93 magic: nx_int, // sanity: caller knows the protocol 94 mask: nx_size, // 8-byte XOR mask 95 pad_count: nx_int, // 0-15 padding bytes 96 payload_len: nx_size, 97} 98 99const NX_CRYPSIS_MAGIC: nx_int = 0x4e58435950534953 // "NXCYPSIS" 100 101// ===== nx_crypsis_encode_size ==================================== 102// 103// Bytes required in dst for a payload of n bytes with pad_count 104// padding bytes. dst_size = sizeof(header) + pad_count + n. 105 106func nx_crypsis_encode_size(payload_len: nx_size, pad_count: nx_int) -> nx_size { 107 // header: 4 fields * 8 bytes = 32 108 return 32 + (pad_count as nx_size) + payload_len 109} 110 111// ===== nx_crypsis_encode ========================================= 112// 113// Encode `payload[0..n]` into `dst` with the supplied mask + padding. 114// dst must be at least nx_crypsis_encode_size(n, pad_count) bytes. 115// pad_count is clamped to 0..15. The encoded form is: 116// [header: magic, mask, pad_count, payload_len] 117// [pad_count random bytes (caller-supplied via pad_bytes[])] 118// [payload XOR mask, repeating mask byte-by-byte] 119 120func nx_crypsis_encode(payload: *u8, 121 payload_len: nx_size, 122 mask: nx_size, 123 pad_bytes: *u8, 124 pad_count: nx_int, 125 dst: *u8, 126 dst_cap: nx_size) -> nx_int { 127 var pad: nx_int = pad_count 128 if pad < 0 { pad = 0 } 129 if pad > 15 { pad = 15 } 130 let need: nx_size = nx_crypsis_encode_size(payload_len, pad) 131 if dst_cap < need { return NX_CRYP_ERR_DST_TOO_SMALL } 132 133 // Write header (32 bytes). 134 let hdr: *NxCrypsisHeader = dst as *NxCrypsisHeader 135 hdr.magic = NX_CRYPSIS_MAGIC 136 hdr.mask = mask 137 hdr.pad_count = pad 138 hdr.payload_len = payload_len 139 140 let body_off: nx_size = 32 141 142 // Copy random padding bytes (caller supplies entropy). 143 var i: nx_size = 0 144 while i < (pad as nx_size) { 145 dst[body_off + i] = pad_bytes[i] 146 i = i + 1 147 } 148 149 // XOR-mask payload byte-by-byte. mask cycles every 8 bytes. 150 var j: nx_size = 0 151 while j < payload_len { 152 let shift: nx_int = ((j as i64) & 7) * 8 153 let mb: nx_int = (mask >> shift) & 255 154 let pb: nx_int = (payload[j] as i64) & 255 155 dst[body_off + (pad as nx_size) + j] = ((pb ^ mb) & 255) as u8 156 j = j + 1 157 } 158 return NX_CRYP_OK 159} 160 161// ===== nx_crypsis_decode ========================================= 162// 163// Inverse: read header from src, validate magic, extract mask + 164// payload_len, XOR-mask back to original bytes in dst. 165 166func nx_crypsis_decode(src: *u8, 167 src_len: nx_size, 168 dst: *u8, 169 dst_cap: nx_size, 170 out_payload_len: *i64) -> nx_int { 171 if src_len < 32 { return NX_CRYP_ERR_DST_TOO_SMALL } 172 let hdr: *NxCrypsisHeader = src as *NxCrypsisHeader 173 if hdr.magic != NX_CRYPSIS_MAGIC { return NX_CRYP_ERR_BAD_MASK } 174 let pad: nx_int = hdr.pad_count 175 let payload_len: nx_size = hdr.payload_len 176 if dst_cap < payload_len { return NX_CRYP_ERR_DST_TOO_SMALL } 177 let body_off: nx_size = 32 + (pad as nx_size) 178 if src_len < body_off + payload_len { return NX_CRYP_ERR_DST_TOO_SMALL } 179 180 let mask: nx_size = hdr.mask 181 var j: nx_size = 0 182 while j < payload_len { 183 let shift: nx_int = ((j as i64) & 7) * 8 184 let mb: nx_int = (mask >> shift) & 255 185 let cb: nx_int = (src[body_off + j] as i64) & 255 186 dst[j] = ((cb ^ mb) & 255) as u8 187 j = j + 1 188 } 189 out_payload_len[0] = payload_len as i64 190 return NX_CRYP_OK 191} 192 193// ===== nx_crypsis_signatures_match =============================== 194// 195// Test predicate: given two crypsis-encoded emissions of the SAME 196// logical payload, do their wire-byte signatures DIFFER? Returns 1 197// if signatures differ (crypsis succeeded), 0 if identical (a 198// detector could still anchor). Smoke uses this to verify that 199// different masks produce different signatures. 200 201func nx_crypsis_signatures_match(a: *u8, a_len: nx_size, 202 b: *u8, b_len: nx_size) -> nx_int { 203 if a_len != b_len { return 0 } 204 var i: nx_size = 0 205 while i < a_len { 206 if a[i] != b[i] { return 0 } 207 i = i + 1 208 } 209 return 1 210}