1 # CAST-256 (RFC 2612, The CAST-256 Encryption Algorithm, 1999), also called 2 # CAST6: a 128-bit block cipher with keys of 128, 160, 192, 224 or 256 bits, 3 # as a cipher for cipher-modes' modes (~LcCast6Key, ~LcCast6.encrypt, 4 # ~LcCast6.decrypt). 5 # 6 # The block is four big-endian words A, B, C, D (a CmBlock). The forward 7 # quad-round Q(i) is 8 # C ^= f1(D), B ^= f2(C), A ^= f3(B), D ^= f1(A) 9 # with key set i's masking and rotation keys, and QBAR(i) is the same four 10 # steps in reverse order; encryption is Q(0) .. Q(5), then QBAR(6) .. 11 # QBAR(11). f1, f2, f3 are CAST-128's round functions of types 1, 2, 3 over 12 # its S1 .. S4 (cast5.bend). The key schedule pads the key with zeros to 13 # eight words A .. H and runs 24 forward octaves W(i) on them, with the masks 14 # Tm (from 2^30 sqrt 2, stepping by 2^30 sqrt 3) and rotations Tr (from 19, 15 # stepping by 17 mod 32); after octaves 2i and 2i + 1, Kr(i) is the low 5 16 # bits of A, C, E, G and Km(i) is H, F, D, B. 17 # 18 # Each step XORs a function of one word into another, so it is undone by 19 # itself, and QBAR(i) undoes Q(i): decryption (the quad-rounds undone last 20 # to first, RFC 2612's decryption) undoes encryption for every key and block, 21 # whatever the S-boxes hold. 22 import Base 23 import bend-osdev-bytes@0.1.0.0/bytes.bend as B 24 import bend-osdev-cipher-modes@0.1.0.0/cipher_modes.bend as M 25 import ./legacy_ciphers.bend as L 26 import ./cast5.bend as C5 27 28 # A quad-round's key set (internal) 29 type LcCast6Ks is Data: 30 LcCast6Ks{m0: U32, m1: U32, m2: U32, m3: U32, r0: U32, r1: U32, r2: U32, r3: U32, t: C5.LcCastTabs} 31 32 # the four steps of Q, each undone by itself 33 def c6.sc(k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock: 34 match k: 35 case LcCast6Ks{m0, m1, m2, m3, r0, r1, r2, r3, t}: 36 match s: 37 case M.CmBlock{a, b, c, +d}: 38 M.CmBlock{a, b, U32.xor(c, C5.cast.f(C5.LcCastF1{}, t, d, m0, r0)), d} 39 40 def c6.sb(k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock: 41 match k: 42 case LcCast6Ks{m0, m1, m2, m3, r0, r1, r2, r3, t}: 43 match s: 44 case M.CmBlock{a, b, +c, d}: 45 M.CmBlock{a, U32.xor(b, C5.cast.f(C5.LcCastF2{}, t, c, m1, r1)), c, d} 46 47 def c6.sa(k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock: 48 match k: 49 case LcCast6Ks{m0, m1, m2, m3, r0, r1, r2, r3, t}: 50 match s: 51 case M.CmBlock{a, +b, c, d}: 52 M.CmBlock{U32.xor(a, C5.cast.f(C5.LcCastF3{}, t, b, m2, r2)), b, c, d} 53 54 def c6.sd(k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock: 55 match k: 56 case LcCast6Ks{m0, m1, m2, m3, r0, r1, r2, r3, t}: 57 match s: 58 case M.CmBlock{+a, b, c, d}: 59 M.CmBlock{a, b, c, U32.xor(d, C5.cast.f(C5.LcCastF1{}, t, a, m3, r3))} 60 61 # Q and QBAR 62 def c6.q(+k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock: 63 c6.sd(k, c6.sa(k, c6.sb(k, c6.sc(k, s)))) 64 65 def c6.qbar(+k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock: 66 c6.sc(k, c6.sb(k, c6.sa(k, c6.sd(k, s)))) 67 68 # The steps of the cipher (internal): Q (fwd True) or QBAR with a key set 69 type LcCast6Op is Data: 70 LcCast6Op{fwd: Bool, k: LcCast6Ks} 71 72 def c6.op(o: LcCast6Op, s: M.CmBlock) -> M.CmBlock: 73 match o: 74 case LcCast6Op{fwd, k}: 75 match fwd: 76 case True{}: 77 c6.q(k, s) 78 case False{}: 79 c6.qbar(k, s) 80 81 def c6.unop(o: LcCast6Op, s: M.CmBlock) -> M.CmBlock: 82 match o: 83 case LcCast6Op{fwd, k}: 84 match fwd: 85 case True{}: 86 c6.qbar(k, s) 87 case False{}: 88 c6.q(k, s) 89 90 def c6.run(os: List<&2, LcCast6Op>, s: M.CmBlock) -> M.CmBlock: 91 match os: 92 case Nil{}: 93 s 94 case Con{o, t}: 95 c6.run(t, c6.op(o, s)) 96 97 def c6.unrun(os: List<&2, LcCast6Op>, s: M.CmBlock) -> M.CmBlock: 98 match os: 99 case Nil{}: 100 s 101 case Con{o, t}: 102 c6.unop(o, c6.unrun(t, s)) 103 104 # The key schedule (RFC 2612, 2.4) 105 106 # word i of xs replaced by v 107 def c6.set(xs: List<&2, U32>, i: Nat, v: U32) -> List<&2, U32>: 108 match xs: 109 case Nil{}: 110 Nil{} 111 case Con{x, t}: 112 match i: 113 case 0n: 114 Con{v, t} 115 case 1n+j: 116 Con{x, c6.set(t, j, v)} 117 118 # KAPPA (the eight words A .. H) and the next Tm and Tr 119 type LcCast6Kst is Data: 120 LcCast6Kst{k: List<&2, U32>, cm: U32, cr: U32} 121 122 # one step of an octave: word dst ^= f_ty(word src), with the next Tm, Tr 123 def c6.w(+t: C5.LcCastTabs, ty: C5.LcCastTy, +dst: Nat, src: Nat, st: LcCast6Kst) -> LcCast6Kst: 124 match st: 125 case LcCast6Kst{+k, +cm, +cr}: 126 LcCast6Kst{c6.set(k, dst, U32.xor(L.lc.at(k, dst), C5.cast.f(ty, t, L.lc.at(k, src), cm, cr))), U32.add(cm, 1859775393), U32.and(31, U32.add(cr, 17))} 127 128 # the forward octave W 129 def c6.octave(+t: C5.LcCastTabs, st: LcCast6Kst) -> LcCast6Kst: 130 +s1 = c6.w(t, C5.LcCastF1{}, 6n, 7n, st) 131 +s2 = c6.w(t, C5.LcCastF2{}, 5n, 6n, s1) 132 +s3 = c6.w(t, C5.LcCastF3{}, 4n, 5n, s2) 133 +s4 = c6.w(t, C5.LcCastF1{}, 3n, 4n, s3) 134 +s5 = c6.w(t, C5.LcCastF2{}, 2n, 3n, s4) 135 +s6 = c6.w(t, C5.LcCastF3{}, 1n, 2n, s5) 136 +s7 = c6.w(t, C5.LcCastF1{}, 0n, 1n, s6) 137 c6.w(t, C5.LcCastF2{}, 7n, 0n, s7) 138 139 # the key set read from KAPPA: Km = H, F, D, B and Kr = A, C, E, G (low 5 bits) 140 def c6.ks(+t: C5.LcCastTabs, +k: List<&2, U32>) -> LcCast6Ks: 141 LcCast6Ks{L.lc.at(k, 7n), L.lc.at(k, 5n), L.lc.at(k, 3n), L.lc.at(k, 1n), U32.and(31, L.lc.at(k, 0n)), U32.and(31, L.lc.at(k, 2n)), U32.and(31, L.lc.at(k, 4n)), U32.and(31, L.lc.at(k, 6n)), t} 142 143 # the key set read from KAPPA after an octave pair 144 def c6.ks.of(+t: C5.LcCastTabs, st: LcCast6Kst) -> LcCast6Ks: 145 match st: 146 case LcCast6Kst{+k, cm, cr}: 147 c6.ks(t, k) 148 149 # the steps from quad-round i on: Q up to i = 5, then QBAR 150 def c6.ops(n: Nat, +i: Nat, +t: C5.LcCastTabs, st: LcCast6Kst) -> List<&2, LcCast6Op>: 151 match n: 152 case 0n: 153 Nil{} 154 case 1n+p: 155 +st2 = c6.octave(t, c6.octave(t, st)) 156 Con{LcCast6Op{Nat.is_lt(i, 6n), c6.ks.of(t, st2)}, c6.ops(p, Nat.add(i, 1n), t, st2)} 157 158 # A CAST-256 key: the steps of the cipher (opaque; built by LcCast6.key) 159 type LcCast6Key is Data: 160 LcCast6Key{ops: List<&2, LcCast6Op>} 161 162 # the eight key words of a key padded to 32 bytes 163 def c6.words(xs: List<&2, U32>) -> List<&2, U32>: 164 match xs: 165 case Con{a, Con{b, Con{c, Con{d, t}}}}: 166 Con{L.lc.be(a, b, c, d), c6.words(t)} 167 case _: 168 Nil{} 169 170 def c6.key.pick(ok: Bool, +n: Nat, +xs: List<&2, U32>) -> Result<&2, &2, L.LcError, LcCast6Key>: 171 match ok: 172 case True{}: 173 Done{LcCast6Key{c6.ops(12n, 0n, C5.cast.tables(), LcCast6Kst{c6.words(B.append(xs, B.zeros(Nat.sub(32n, n)))), 1518500249, 19})}} 174 case False{}: 175 Fail{L.LcBadKeyLength{n}} 176 177 # The cipher 178 179 # a key of 16, 20, 24, 28 or 32 bytes 180 def LcCast6.key(o: B.Octets) -> Result<&2, &2, L.LcError, LcCast6Key>: 181 +xs = B.Octets.to_list(o) 182 +n = B.len(xs) 183 c6.key.pick(Bool.and(Bool.and(Nat.is_le(16n, n), Nat.is_le(n, 32n)), Nat.is_eq(Nat.mod(n, 4n), 0n)), n, xs) 184 185 def LcCast6.encrypt(k: LcCast6Key, b: M.CmBlock) -> M.CmBlock: 186 match k: 187 case LcCast6Key{ops}: 188 c6.run(ops, b) 189 190 def LcCast6.decrypt(k: LcCast6Key, b: M.CmBlock) -> M.CmBlock: 191 match k: 192 case LcCast6Key{ops}: 193 c6.unrun(ops, b)