1 # Blowfish: six of Schneier's vectors (keys of 8, 1, 3 and 24 bytes), both 2 # ways (the ciphertext, then the plaintext decrypted back), and Blowfish 3 # through modes64.bend's CBC over 16 KB both ways. 4 import Base 5 import bend-osdev-bytes@0.1.1.0/bytes.bend as B 6 import bend-osdev-cipher-core@0.1.0.0/cipher_core.bend as L 7 import bend-osdev-cipher-core@0.1.0.0/modes64.bend as X 8 import ./blowfish.bend as BF 9 10 def err(e: L.LcError) -> String: 11 match e: 12 case L.LcBadKeyLength{n}: 13 "bad key " ++ Nat.show(n) 14 case L.LcBadIvLength{n}: 15 "bad iv " ++ Nat.show(n) 16 case L.LcNotBlockMultiple{n}: 17 "not whole blocks " ++ Nat.show(n) 18 19 def b64.of(m: Maybe<&2, L.LcBlock64>) -> L.LcBlock64: 20 match m: 21 case None{}: 22 L.LcBlock64.zero() 23 case Some{b}: 24 b 25 26 def b64(o: B.Octets) -> L.LcBlock64: 27 b64.of(L.LcBlock64.of_octets(o)) 28 29 def key(~K: Data, r: Result<&2, &2, L.LcError, K>, d: K) -> K: 30 match r: 31 case Fail{e}: 32 d 33 case Done{k}: 34 k 35 36 def both64(~K: Data, ~enc: K -> L.LcBlock64 -> L.LcBlock64, ~dec: K -> L.LcBlock64 -> L.LcBlock64, +k: K, b: L.LcBlock64) -> String: 37 +c = enc(k, b) 38 L.LcBlock64.hex(c) ++ " " ++ L.LcBlock64.hex(dec(k, c)) 39 40 def blowfish_key(k: B.Octets) -> BF.LcBlowfishKey: 41 key(~BF.LcBlowfishKey, BF.LcBlowfish.key_any(k), BF.LcBlowfishKey{Nil{}}) 42 43 def blowfish(k: B.Octets, b: B.Octets) -> String: 44 both64(~BF.LcBlowfishKey, ~BF.LcBlowfish.encrypt, ~BF.LcBlowfish.decrypt, blowfish_key(k), b64(b)) 45 46 # n bytes, byte i = 7i + 3 (mod 256) 47 def big.go(n: Nat, +i: U32, acc: List<&2, U32>) -> List<&2, U32>: 48 match n: 49 case 0n: 50 B.rev(acc) 51 case 1n+p: 52 big.go(p, U32.add(i, 1), Con{U32.and(255, U32.add(U32.mul(i, 7), 3)), acc}) 53 54 def big(n: Nat) -> B.Octets: 55 B.Octets.of_list(big.go(n, 0, Nil{})) 56 57 def verdict(b: Bool) -> String: 58 match b: 59 case True{}: 60 "ok" 61 case False{}: 62 "differs" 63 64 def last8(r: Result<&2, &2, L.LcError, B.Octets>) -> String: 65 match r: 66 case Fail{e}: 67 err(e) 68 case Done{+o}: 69 B.Octets.hex(B.Octets.drop(Nat.sub(B.Octets.len(o), 8n), o)) 70 71 def same64(r: Result<&2, &2, L.LcError, B.Octets>, +m: B.Octets) -> String: 72 match r: 73 case Fail{e}: 74 err(e) 75 case Done{o}: 76 verdict(B.Octets.is_eq(o, m)) 77 78 def cbc64(~K: Data, ~enc: K -> L.LcBlock64 -> L.LcBlock64, ~dec: K -> L.LcBlock64 -> L.LcBlock64, +k: K, n: Nat) -> String: 79 +m = big(n) 80 +c = X.Lc64.cbc_encrypt(~K, ~enc, k, B.Octets.zeros(8n), m) 81 last8(c) ++ " " ++ same64(Result.bind(&2, &2, L.LcError, B.Octets, B.Octets, c, x => X.Lc64.cbc_decrypt(~K, ~dec, k, B.Octets.zeros(8n), x)), m) 82 83 def blowfish_cbc(k: B.Octets, n: Nat) -> String: 84 cbc64(~BF.LcBlowfishKey, ~BF.LcBlowfish.encrypt, ~BF.LcBlowfish.decrypt, blowfish_key(k), n) 85 86 # BEGIN GENERATED by gen/vectors.py: check 87 def main() -> IO(Unit): 88 do IO: 89 IO.print(blowfish(B.Octets.of_list([0, 0, 0, 0, 0, 0, 0, 0]), B.Octets.of_list([0, 0, 0, 0, 0, 0, 0, 0]))) 90 IO.print(blowfish(B.Octets.of_list([255, 255, 255, 255, 255, 255, 255, 255]), B.Octets.of_list([255, 255, 255, 255, 255, 255, 255, 255]))) 91 IO.print(blowfish(B.Octets.of_list([254, 220, 186, 152, 118, 84, 50, 16]), B.Octets.of_list([1, 35, 69, 103, 137, 171, 205, 239]))) 92 IO.print(blowfish(B.Octets.of_list([240]), B.Octets.of_list([254, 220, 186, 152, 118, 84, 50, 16]))) 93 IO.print(blowfish(B.Octets.of_list([240, 225, 210]), B.Octets.of_list([254, 220, 186, 152, 118, 84, 50, 16]))) 94 IO.print(blowfish(B.Octets.of_list([240, 225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 0, 17, 34, 51, 68, 85, 102, 119]), B.Octets.of_list([254, 220, 186, 152, 118, 84, 50, 16]))) 95 IO.print(blowfish_cbc(B.Octets.of_list([1, 35, 69, 103, 137, 171, 205, 239]), 16384n)) 96 97 #|4ef997456198dd78 0000000000000000 98 #|51866fd5b85ecb8a ffffffffffffffff 99 #|0aceab0fc6a0a28d 0123456789abcdef 100 #|f9ad597c49db005e fedcba9876543210 101 #|e9c2b70a1bc65cf3 fedcba9876543210 102 #|05044b62fa52d080 fedcba9876543210 103 #|d9e0b4045974d5f0 ok 104 # END GENERATED: check