v / vlib / x / crypto / poly1305 / custom.v
157 lines · 142 sloc · 5.28 KB · 19f080ffb8f8f01976692f6b79d9f857c685e109
Raw
1module poly1305
2
3import math.bits
4import math.unsigned
5
6const uint192_zero = Uint192{}
7
8// Uint192 is a structure representing 192 bits of unsigned integer.
9// It serves as a custom allocator for the poly1305 implementation to use.
10// However, it can be used to complement and expand the standard `math.unsigned`
11// module with a more complete and extensive range of handling of unsigned integers.
12struct Uint192 {
13mut:
14 lo u64
15 mi u64
16 hi u64
17}
18
19// Here, we define several required functionalities of the custom allocator.
20
21// add_checked returns u+v with carry
22fn (u Uint192) add_checked(v Uint192, c u64) (Uint192, u64) {
23 lo, c0 := bits.add_64(u.lo, v.lo, c)
24 mi, c1 := bits.add_64(u.mi, v.mi, c0)
25 hi, c2 := bits.add_64(u.hi, v.hi, c1)
26 x := Uint192{lo, mi, hi}
27 return x, c2
28}
29
30// add_128_checked returns u+v with carry
31fn (u Uint192) add_128_checked(v unsigned.Uint128, c u64) (Uint192, u64) {
32 lo, c0 := bits.add_64(u.lo, v.lo, c)
33 mi, c1 := bits.add_64(u.mi, v.hi, c0)
34 hi, c2 := bits.add_64(u.hi, 0, c1)
35 x := Uint192{lo, mi, hi}
36 return x, c2
37}
38
39fn (u Uint192) add_64_checked(v u64, c u64) (Uint192, u64) {
40 lo, c0 := bits.add_64(u.lo, v, c)
41 mi, c1 := bits.add_64(u.mi, 0, c0)
42 hi, c2 := bits.add_64(u.hi, 0, c1)
43 x := Uint192{lo, mi, hi}
44 return x, c2
45}
46
47fn (u Uint192) sub_checked(v Uint192) (Uint192, u64) {
48 lo, b0 := bits.sub_64(u.lo, v.lo, 0)
49 mi, b1 := bits.sub_64(u.mi, v.mi, b0)
50 hi, b2 := bits.sub_64(u.hi, v.hi, b1)
51 return Uint192{lo, mi, hi}, b2
52}
53
54// mul_64_checked returns `u*v` even if the result size is over > 192 bits.
55// It returns a `(Uin192, u64)` pair where the former stores the low 192 bits and
56// the rest of high bits stored in the `u64` part. You can check the value of the `u64` part
57// for `c != 0`, it's mean, the product of `u*v` is overflowing 192 bits.
58fn (u Uint192) mul_64_checked(v u64) (Uint192, u64) {
59 // see mul_128_checked for the logic
60 m0 := u128_from_64_mul(u.lo, v)
61 m1 := u128_from_64_mul(u.mi, v)
62 m2 := u128_from_64_mul(u.hi, v)
63 // propagates carry
64 t0, c0 := bits.add_64(m0.lo, 0, 0)
65 t1, c1 := bits.add_64(m0.hi, m1.lo, c0)
66 t2, c2 := bits.add_64(m1.hi, m2.lo, c1)
67 t3, c3 := bits.add_64(m2.hi, 0, c2)
68 // something bad happen if the last c3 is non null
69 if c3 != 0 {
70 panic('Uint192: unexpected overflow')
71 }
72 x := Uint192{
73 lo: t0
74 mi: t1
75 hi: t2
76 }
77 return x, t3
78}
79
80// mul_128_checked is a generic product of 192 bits u by 128 bits v.
81// Its returns u*v even if the result is over > 192 bits, and stores the remaining
82// high bits of the result into the Uint128 structure.
83fn (u Uint192) mul_128_checked(v unsigned.Uint128) (Uint192, unsigned.Uint128) {
84 // u.hi u.mi u.lo
85 // v.hi v.lo
86 // ---------------------------------------------------------- x
87 // uhi*vlo umi*vlo ulo*vlo
88 // uhi*vhi umi*vhi ulo*vhi
89 // ==========================================================
90 // m3 m2 m1 m0 // Uint128
91 //
92 // ---------------------------------------------------------- +
93 // m3.hi m2.hi m1.hi m0.hi
94 // m3.lo m2.lo m1.lo m0.lo
95 // ==========================================================
96 // t4 t3 t2 t1 t0
97 //
98 ulovlo := u128_from_64_mul(u.lo, v.lo)
99 umivlo := u128_from_64_mul(u.mi, v.lo)
100 uhivlo := u128_from_64_mul(u.hi, v.lo)
101
102 ulovhi := u128_from_64_mul(u.lo, v.hi)
103 umivhi := u128_from_64_mul(u.mi, v.hi)
104 uhivhi := u128_from_64_mul(u.hi, v.hi)
105
106 m0 := ulovlo
107 m1, c1 := unsigned.add_128(umivlo, ulovhi, 0)
108 m2, c2 := unsigned.add_128(uhivlo, umivhi, c1)
109 m3, c3 := unsigned.add_128(uhivhi, unsigned.uint128_zero, c2)
110 if c3 != 0 {
111 panic('Uint192: unexpected overflow')
112 }
113 // Note about the multiplication results in Poly1305 context.
114 // In the properly clamped 128 bits of v, (called "r" in the poly1305 context) and
115 // safely reduced form of high part of the 192 bits accumulator u (u.hi), where only
116 // maximum of four low bits of u.hi is set, and we can assume (and confirm with tests)
117 // if the high bit part of the product of uhi*vhi and uhi*vlo is not set,
118 // ie, x = (uhi*vhi).hi == 0 and y = (uhi*vlo).hi == 0.
119 // and the 128 bits addition of `m2 = uhi*vlo + umi*vhi`, would also not overflow 128 bits,
120 // thats also mean, the last carry is null for the reason and m3 = (uhi*vhi).hi is also null
121 //
122 t0 := m0.lo
123 t1, c4 := bits.add_64(m0.hi, m1.lo, 0)
124 t2, c5 := bits.add_64(m1.hi, m2.lo, c4)
125 t3, c6 := bits.add_64(m2.hi, m3.lo, c5)
126 t4, c7 := bits.add_64(m3.hi, 0, c6)
127 if c7 != 0 {
128 panic('Uint192: unexpected overflow')
129 }
130 // based on previous notes, for poly1305 context, it tells us if the product
131 // doesn't have a fitfh limb (t4), ie t4 == null, and we can safely ignore it.
132 //
133 x := Uint192{
134 lo: t0
135 mi: t1
136 hi: t2
137 }
138 hb := unsigned.uint128_new(t3, t4)
139 return x, hb
140}
141
142// u128_from_64_mul creates new Uint128 from 64x64 bit product of x*y
143fn u128_from_64_mul(x u64, y u64) unsigned.Uint128 {
144 hi, lo := bits.mul_64(x, y)
145 return unsigned.uint128_new(lo, hi)
146}
147
148// select_64 returns x if v == 1 and y if v == 0, in constant time.
149fn select_64(v u64, x u64, y u64) u64 {
150 return ~(v - 1) & x | (v - 1) & y
151}
152
153fn shift_right_by2(mut a unsigned.Uint128) unsigned.Uint128 {
154 a.lo = a.lo >> 2 | (a.hi & 3) << 62
155 a.hi = a.hi >> 2
156 return a
157}
158