v / vlib / x / crypto / mldsa / sampling.v
146 lines · 134 sloc · 2.75 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.sha3
9
10// algo. 30: RejNTTPoly (s. 7.3)
11@[direct_array_access]
12fn sample_ntt(rho []u8, s u8, r u8) NttElement {
13 mut g := sha3.new_shake128()
14 g.write(rho)
15 g.write([s, r])
16
17 mut a := NttElement{}
18 mut j := 0
19 mut buf := g.read(168)
20 mut off := 0
21 for j < n {
22 if off + 2 >= buf.len {
23 buf = g.read(168)
24 off = 0
25 }
26 v := u32(buf[off]) | (u32(buf[off + 1]) << 8) | (u32(buf[off + 2]) << 16)
27 off += 3
28 candidate := v & 0x7fffff
29 if candidate < q {
30 a[j] = field_to_montgomery(candidate) or { continue }
31 j++
32 }
33 }
34 return a
35}
36
37// algo. 31: RejBoundedPoly (s. 7.3)
38@[direct_array_access]
39fn sample_bounded_poly(rho []u8, r u8, p Params) RingElement {
40 mut h := sha3.new_shake256()
41 h.write(rho)
42 h.write([r, 0])
43
44 mut a := RingElement{}
45 mut j := 0
46 mut buf := h.read(136)
47 mut off := 0
48 for {
49 if off >= buf.len {
50 buf = h.read(136)
51 off = 0
52 }
53 z0 := buf[off] & 0x0f
54 z1 := buf[off] >> 4
55 off++
56
57 coeff, ok := coeff_from_half_byte(z0, p)
58 if ok {
59 a[j] = coeff
60 j++
61 }
62 if j >= n {
63 break
64 }
65
66 coeff2, ok2 := coeff_from_half_byte(z1, p)
67 if ok2 {
68 a[j] = coeff2
69 j++
70 }
71 if j >= n {
72 break
73 }
74 }
75 return a
76}
77
78// algo. 29: SampleInBall (s. 7.3)
79@[direct_array_access]
80fn sample_in_ball(rho []u8, p Params) RingElement {
81 mut h := sha3.new_shake256()
82 h.write(rho)
83 s := h.read(8)
84
85 // pre-read ~2x expected bytes to avoid per-byte allocations
86 mut buf := h.read(p.tau * 2)
87 mut off := 0
88
89 mut c := RingElement{}
90 for i := 256 - p.tau; i < 256; i++ {
91 for {
92 if off >= buf.len {
93 buf = h.read(256)
94 off = 0
95 }
96 j := buf[off]
97 off++
98 if j <= u8(i) {
99 c[i] = c[j]
100 bit_idx := i + p.tau - 256
101 bit := (s[bit_idx / 8] >> (bit_idx % 8)) & 1
102 if bit == 0 {
103 c[j] = mont_one
104 } else {
105 c[j] = mont_minus_one
106 }
107 break
108 }
109 }
110 }
111 return c
112}
113
114// algo. 15: CoeffFromHalfByte (s. 7.1)
115fn coeff_from_half_byte(b u8, p Params) (FieldElement, bool) {
116 match p.eta {
117 2 {
118 if b > 14 {
119 return FieldElement(0), false
120 }
121 quotient := (u32(b) * 0x3334) >> 16 // barrett b % 5
122 remainder := u32(b) - quotient * 5
123 return field_sub_to_montgomery(2, remainder), true
124 }
125 4 {
126 if b > 8 {
127 return FieldElement(0), false
128 }
129 return field_sub_to_montgomery(4, u32(b)), true
130 }
131 else {
132 panic('mldsa: unsupported eta') // unreachable
133 }
134 }
135}
136
137// algo. 32: ExpandA (s. 7.3)
138fn compute_matrix_a(rho []u8, p Params) []NttElement {
139 mut a := []NttElement{len: p.k * p.l}
140 for r in 0 .. p.k {
141 for s in 0 .. p.l {
142 a[r * p.l + s] = sample_ntt(rho, u8(s), u8(r))
143 }
144 }
145 return a
146}
147