v2 / vlib / x / crypto / mldsa / field.v
159 lines · 134 sloc · 3.91 KB · b615cd08d134956354a72dcc42a6a6ad4e39cb64
Raw
1// Copyright 2025 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4//
5// Ported to V from Go's crypto/internal/fips140/mldsa.
6module mldsa
7
8import crypto.internal.subtle
9
10// s. 2.3, appendix a
11const q = u32(8380417) // 2^23 - 2^13 + 1
12const rr = u32(2365951) // R^2 mod q (R = 2^32)
13const q_neg_inv = u32(4236238847) // -q^-1 mod R (appendix a: QINV = 58728449)
14const mont_one = u32(4193792) // R mod q
15const mont_minus_one = u32(4186625) // (q-1)*R mod q
16const n = 256
17const d = 13
18
19// signing uses rejection sampling that succeeds with p =~ 1/5.22
20// per attempt (worst case: ML-DSA-65, over 10k sigs)
21// P(no converg. after 512 iters) = (1 - 1/5.22)^512 =~ 2^-157
22// for ref. ML-DSA-44 security is 2^-128
23const max_sign_attempts = 512
24
25type FieldElement = u32
26type RingElement = [256]u32
27type NttElement = [256]u32
28
29fn field_to_montgomery(a u32) !FieldElement {
30 if a >= q {
31 return error('unreduced field element ${a}')
32 }
33 return field_montgomery_mul(FieldElement(a), rr)
34}
35
36fn field_sub_to_montgomery(a u32, b u32) FieldElement {
37 x := a - b + q
38 return field_montgomery_mul(FieldElement(x), rr)
39}
40
41fn field_from_montgomery(a FieldElement) u32 {
42 return u32(field_montgomery_reduce(u64(a)))
43}
44
45fn field_centered_mod(r FieldElement) i32 {
46 x := i32(field_from_montgomery(r))
47 return ct_select_leq(x, i32(q / 2), x, x - i32(q))
48}
49
50fn field_infinity_norm(r FieldElement) u32 {
51 x := i32(field_from_montgomery(r))
52 return u32(ct_select_leq(x, i32(q / 2), x, i32(q) - x))
53}
54
55fn field_reduce_once(a u32) FieldElement {
56 if a >= q {
57 return FieldElement(a - q)
58 }
59 return FieldElement(a)
60}
61
62fn field_add(a FieldElement, b FieldElement) FieldElement {
63 return field_reduce_once(u32(a) + u32(b))
64}
65
66fn field_sub(a FieldElement, b FieldElement) FieldElement {
67 return field_reduce_once(u32(a) - u32(b) + q)
68}
69
70fn field_montgomery_mul(a FieldElement, b FieldElement) FieldElement {
71 x := u64(a) * u64(b)
72 return field_montgomery_reduce(x)
73}
74
75// algo. 49: MontgomeryReduce
76fn field_montgomery_reduce(x u64) FieldElement {
77 t := u32(x) * q_neg_inv
78 u_ := (x + u64(t) * u64(q)) >> 32
79 return field_reduce_once(u32(u_))
80}
81
82fn field_montgomery_mul_sub(a FieldElement, b FieldElement, c FieldElement) FieldElement {
83 x := u64(a) * u64(u32(b) - u32(c) + q)
84 return field_montgomery_reduce(x)
85}
86
87fn field_montgomery_add_mul(a FieldElement, b FieldElement, c FieldElement, d_ FieldElement) FieldElement {
88 x := u64(a) * u64(b) + u64(c) * u64(d_)
89 return field_montgomery_reduce(x)
90}
91
92@[direct_array_access]
93fn poly_add_ring(a RingElement, b RingElement) RingElement {
94 mut s := RingElement{}
95 for i in 0 .. n {
96 s[i] = field_add(a[i], b[i])
97 }
98 return s
99}
100
101@[direct_array_access]
102fn poly_add_ntt(a NttElement, b NttElement) NttElement {
103 mut s := NttElement{}
104 for i in 0 .. n {
105 s[i] = field_add(a[i], b[i])
106 }
107 return s
108}
109
110@[direct_array_access]
111fn poly_sub_ring(a RingElement, b RingElement) RingElement {
112 mut s := RingElement{}
113 for i in 0 .. n {
114 s[i] = field_sub(a[i], b[i])
115 }
116 return s
117}
118
119@[direct_array_access]
120fn poly_sub_ntt(a NttElement, b NttElement) NttElement {
121 mut s := NttElement{}
122 for i in 0 .. n {
123 s[i] = field_sub(a[i], b[i])
124 }
125 return s
126}
127
128// algo. 45: MultiplyNTT
129@[direct_array_access]
130fn ntt_mul(a NttElement, b NttElement) NttElement {
131 mut p := NttElement{}
132 for i in 0 .. n {
133 p[i] = field_montgomery_mul(a[i], b[i])
134 }
135 return p
136}
137
138@[direct_array_access]
139fn coefficients_exceed_bound(w RingElement, bound u32) bool {
140 for i in 0 .. n {
141 if field_infinity_norm(w[i]) >= bound {
142 return true
143 }
144 }
145 return false
146}
147
148fn ct_select_leq(a i32, b i32, yes i32, no i32) i32 {
149 return if subtle.constant_time_less_or_eq(int(a), int(b)) == 1 { yes } else { no }
150}
151
152fn ct_select_eq(a u32, b u32, yes u32, no u32) u32 {
153 return u32(subtle.constant_time_select(subtle.constant_time_eq(int(a), int(b)), int(yes),
154 int(no)))
155}
156
157fn ct_abs(x i32) u32 {
158 return u32(ct_select_leq(0, x, x, -x))
159}
160