cast6.bend

bend-osdev-legacy-ciphers@0.1.0.0 · 193 lines · 25 definitions

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.
22import Base
23import bend-osdev-bytes@0.1.0.0/bytes.bend as B
24import bend-osdev-cipher-modes@0.1.0.0/cipher_modes.bend as M
25import bend-osdev-legacy-ciphers@0.1.0.0/legacy_ciphers.bend as L
26import bend-osdev-legacy-ciphers@0.1.0.0/cast5.bend as C5
27
28# A quad-round's key set (internal)
29type 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
33def 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
40def 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
47def 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
54def 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
62def c6.q(+k: LcCast6Ks, s: M.CmBlock) -> M.CmBlock:
63  c6.sd(k, c6.sa(k, c6.sb(k, c6.sc(k, s))))
64
65def 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
69type LcCast6Op is Data:
70  LcCast6Op{fwd: Bool, k: LcCast6Ks}
71
72def 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
81def 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
90def 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
97def 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
107def 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
119type 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
123def 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
129def 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)
140def 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
144def 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
150def 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)
159type LcCast6Key is Data:
160  LcCast6Key{ops: List<&2, LcCast6Op>}
161
162# the eight key words of a key padded to 32 bytes
163def 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
170def 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
180def 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
185def LcCast6.encrypt(k: LcCast6Key, b: M.CmBlock) -> M.CmBlock:
186  match k:
187    case LcCast6Key{ops}:
188      c6.run(ops, b)
189
190def LcCast6.decrypt(k: LcCast6Key, b: M.CmBlock) -> M.CmBlock:
191  match k:
192    case LcCast6Key{ops}:
193      c6.unrun(ops, b)