let pitable = [| 0xd9; 0x78; 0xf9; 0xc4; 0x19; 0xdd; 0xb5; 0xed; 0x28; 0xe9; 0xfd; 0x79; 0x4a; 0xa0; 0xd8; 0x9d; 0xc6; 0x7e; 0x37; 0x83; 0x2b; 0x76; 0x53; 0x8e; 0x62; 0x4c; 0x64; 0x88; 0x44; 0x8b; 0xfb; 0xa2; 0x17; 0x9a; 0x59; 0xf5; 0x87; 0xb3; 0x4f; 0x13; 0x61; 0x45; 0x6d; 0x8d; 0x09; 0x81; 0x7d; 0x32; 0xbd; 0x8f; 0x40; 0xeb; 0x86; 0xb7; 0x7b; 0x0b; 0xf0; 0x95; 0x21; 0x22; 0x5c; 0x6b; 0x4e; 0x82; 0x54; 0xd6; 0x65; 0x93; 0xce; 0x60; 0xb2; 0x1c; 0x73; 0x56; 0xc0; 0x14; 0xa7; 0x8c; 0xf1; 0xdc; 0x12; 0x75; 0xca; 0x1f; 0x3b; 0xbe; 0xe4; 0xd1; 0x42; 0x3d; 0xd4; 0x30; 0xa3; 0x3c; 0xb6; 0x26; 0x6f; 0xbf; 0x0e; 0xda; 0x46; 0x69; 0x07; 0x57; 0x27; 0xf2; 0x1d; 0x9b; 0xbc; 0x94; 0x43; 0x03; 0xf8; 0x11; 0xc7; 0xf6; 0x90; 0xef; 0x3e; 0xe7; 0x06; 0xc3; 0xd5; 0x2f; 0xc8; 0x66; 0x1e; 0xd7; 0x08; 0xe8; 0xea; 0xde; 0x80; 0x52; 0xee; 0xf7; 0x84; 0xaa; 0x72; 0xac; 0x35; 0x4d; 0x6a; 0x2a; 0x96; 0x1a; 0xd2; 0x71; 0x5a; 0x15; 0x49; 0x74; 0x4b; 0x9f; 0xd0; 0x5e; 0x04; 0x18; 0xa4; 0xec; 0xc2; 0xe0; 0x41; 0x6e; 0x0f; 0x51; 0xcb; 0xcc; 0x24; 0x91; 0xaf; 0x50; 0xa1; 0xf4; 0x70; 0x39; 0x99; 0x7c; 0x3a; 0x85; 0x23; 0xb8; 0xb4; 0x7a; 0xfc; 0x02; 0x36; 0x5b; 0x25; 0x55; 0x97; 0x31; 0x2d; 0x5d; 0xfa; 0x98; 0xe3; 0x8a; 0x92; 0xae; 0x05; 0xdf; 0x29; 0x10; 0x67; 0x6c; 0xba; 0xc9; 0xd3; 0x00; 0xe6; 0xcf; 0xe1; 0x9e; 0xa8; 0x2c; 0x63; 0x16; 0x01; 0x3f; 0x58; 0xe2; 0x89; 0xa9; 0x0d; 0x38; 0x34; 0x1b; 0xab; 0x33; 0xff; 0xb0; 0xbb; 0x48; 0x0c; 0x5f; 0xb9; 0xb1; 0xcd; 0x2e; 0xc5; 0xf3; 0xdb; 0x47; 0xe5; 0xa5; 0x9c; 0x77; 0x0a; 0xa6; 0x20; 0x68; 0xfe; 0x7f; 0xc1; 0xad |] (* effective is sometimes named t1 *) let tm effective = let t8 = (effective + 7) / 8 in (* RFC says (TM = 255 MOD 2^(8 + effective - 8*T8)) *) let bits = 8 + effective - 8 * t8 in (* likely there's a smarter way to do this *) let rec c acc = function | 0 -> acc | n -> c ((acc lsl 1) + 1) (pred n) in t8, c 0 bits (* L[i] is the i-th byte of the key; K[i] is the i-th 16-bit-word of the key *) let key_expansion effective key = (* result is a 128 byte key, where we need the words.. *) let t = String.length key in let l = Array.init 128 (fun idx -> if idx < t then String.get_uint8 key idx else 0) in let t8, tm = tm effective in for i = t to 127 do l.(i) <- pitable.((l.(i - 1) + l.(i - t)) mod 256) done; l.(128 - t8) <- pitable.(l.(128 - t8) land tm); for i = 127 - t8 downto 0 do l.(i) <- pitable.(l.(i + 1) lxor l.(i + t8)); done; Array.init 64 (fun idx -> l.(2 * idx) + 256 * l.(2 * idx + 1)) let mod16 f = 0xFFFF land f let rol16 x k = mod16 ((x lsl k) lor (x lsr (16 - k))) let ror16 x k = mod16 ((x lsr k) lor (x lsl (16 - k))) let not16 x = mod16 (lnot x) let s = Array.init 4 (function 0 -> 1 | 1 -> 2 | 2 -> 3 | 3 -> 5 | _ -> assert false) let pmod a = let b = 4 in let r = a mod b in if r < 0 then (r + b) mod b else r (* only used for encryption which we don't support let mix r i k j = r.(i) <- mod16 (r.(i) + k.(j) + r.(pmod (i - 1)) land r.(pmod (i - 2)) + (not16 r.(pmod (i - 1))) land r.(pmod (i - 3))); let j = succ j in r.(i) <- rol16 r.(i) s.(i); j let mix_round r k j = let j' = mix r 0 k j in let j'' = mix r 1 k j' in let j''' = mix r 2 k j'' in let j'''' = mix r 3 k j''' in j'''' let mash r i k = r.(i) <- mod16 (r.(i) + k.(r.(pmod (i - 1)) land 63)) let mash_round r k = mash r 0 k; mash r 1 k; mash r 2 k; mash r 3 k let encrypt_one ~key ~data = let r = Array.init 4 (fun idx -> Cstruct.LE.get_uint16 data (idx * 2)) in let j = 0 in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in mash_round r key; let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in mash_round r key; let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let j = mix_round r key j in let _j = mix_round r key j in let out = Cstruct.create 8 in Cstruct.LE.set_uint16 out 0 r.(0); Cstruct.LE.set_uint16 out 2 r.(1); Cstruct.LE.set_uint16 out 4 r.(2); Cstruct.LE.set_uint16 out 6 r.(3); out *) let r_mix r i k j = r.(i) <- ror16 r.(i) s.(i); r.(i) <- mod16 (r.(i) - k.(j) - (r.(pmod (i - 1)) land r.(pmod (i - 2))) - (not16 r.(pmod (i - 1)) land (r.(pmod (i - 3))))); pred j let r_mix_round r k j = let j' = r_mix r 3 k j in let j'' = r_mix r 2 k j' in let j''' = r_mix r 1 k j'' in let j'''' = r_mix r 0 k j''' in j'''' let r_mash r i k = r.(i) <- mod16 (r.(i) - k.(r.(pmod (i - 1)) land 63)) let r_mash_round r k = r_mash r 3 k; r_mash r 2 k; r_mash r 1 k; r_mash r 0 k let decrypt_one ~key ~data ?(off = 0) dst = let r = Array.init 4 (fun idx -> String.get_uint16_le data (off + idx * 2)) in let j = 63 in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in r_mash_round r key; let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in r_mash_round r key; let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let j = r_mix_round r key j in let _j = r_mix_round r key j in Bytes.set_uint16_le dst (off + 0) r.(0); Bytes.set_uint16_le dst (off + 2) r.(1); Bytes.set_uint16_le dst (off + 4) r.(2); Bytes.set_uint16_le dst (off + 6) r.(3) let decrypt_cbc ?(effective = 128) ~key ~iv data = let block = 8 in let key = key_expansion effective key in let l = String.length data in let dst = Bytes.create l in for i = 0 to pred ((l + pred block) / block) do decrypt_one ~key ~data ~off:(i * block) dst done; Mirage_crypto.Uncommon.unsafe_xor_into iv ~src_off:0 dst ~dst_off:0 block; Mirage_crypto.Uncommon.unsafe_xor_into data ~src_off:0 dst ~dst_off:block (l - block); Bytes.unsafe_to_string dst