160 lines
3.9 KiB
V
160 lines
3.9 KiB
V
// Copyright 2025 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
//
|
|
// Ported to V from Go's crypto/internal/fips140/mldsa.
|
|
module mldsa
|
|
|
|
import crypto.internal.subtle
|
|
|
|
// s. 2.3, appendix a
|
|
const q = u32(8380417) // 2^23 - 2^13 + 1
|
|
const rr = u32(2365951) // R^2 mod q (R = 2^32)
|
|
const q_neg_inv = u32(4236238847) // -q^-1 mod R (appendix a: QINV = 58728449)
|
|
const mont_one = u32(4193792) // R mod q
|
|
const mont_minus_one = u32(4186625) // (q-1)*R mod q
|
|
const n = 256
|
|
const d = 13
|
|
|
|
// signing uses rejection sampling that succeeds with p =~ 1/5.22
|
|
// per attempt (worst case: ML-DSA-65, over 10k sigs)
|
|
// P(no converg. after 512 iters) = (1 - 1/5.22)^512 =~ 2^-157
|
|
// for ref. ML-DSA-44 security is 2^-128
|
|
const max_sign_attempts = 512
|
|
|
|
type FieldElement = u32
|
|
type RingElement = [256]u32
|
|
type NttElement = [256]u32
|
|
|
|
fn field_to_montgomery(a u32) !FieldElement {
|
|
if a >= q {
|
|
return error('unreduced field element ${a}')
|
|
}
|
|
return field_montgomery_mul(FieldElement(a), rr)
|
|
}
|
|
|
|
fn field_sub_to_montgomery(a u32, b u32) FieldElement {
|
|
x := a - b + q
|
|
return field_montgomery_mul(FieldElement(x), rr)
|
|
}
|
|
|
|
fn field_from_montgomery(a FieldElement) u32 {
|
|
return u32(field_montgomery_reduce(u64(a)))
|
|
}
|
|
|
|
fn field_centered_mod(r FieldElement) i32 {
|
|
x := i32(field_from_montgomery(r))
|
|
return ct_select_leq(x, i32(q / 2), x, x - i32(q))
|
|
}
|
|
|
|
fn field_infinity_norm(r FieldElement) u32 {
|
|
x := i32(field_from_montgomery(r))
|
|
return u32(ct_select_leq(x, i32(q / 2), x, i32(q) - x))
|
|
}
|
|
|
|
fn field_reduce_once(a u32) FieldElement {
|
|
if a >= q {
|
|
return FieldElement(a - q)
|
|
}
|
|
return FieldElement(a)
|
|
}
|
|
|
|
fn field_add(a FieldElement, b FieldElement) FieldElement {
|
|
return field_reduce_once(u32(a) + u32(b))
|
|
}
|
|
|
|
fn field_sub(a FieldElement, b FieldElement) FieldElement {
|
|
return field_reduce_once(u32(a) - u32(b) + q)
|
|
}
|
|
|
|
fn field_montgomery_mul(a FieldElement, b FieldElement) FieldElement {
|
|
x := u64(a) * u64(b)
|
|
return field_montgomery_reduce(x)
|
|
}
|
|
|
|
// algo. 49: MontgomeryReduce
|
|
fn field_montgomery_reduce(x u64) FieldElement {
|
|
t := u32(x) * q_neg_inv
|
|
u_ := (x + u64(t) * u64(q)) >> 32
|
|
return field_reduce_once(u32(u_))
|
|
}
|
|
|
|
fn field_montgomery_mul_sub(a FieldElement, b FieldElement, c FieldElement) FieldElement {
|
|
x := u64(a) * u64(u32(b) - u32(c) + q)
|
|
return field_montgomery_reduce(x)
|
|
}
|
|
|
|
fn field_montgomery_add_mul(a FieldElement, b FieldElement, c FieldElement, d_ FieldElement) FieldElement {
|
|
x := u64(a) * u64(b) + u64(c) * u64(d_)
|
|
return field_montgomery_reduce(x)
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn poly_add_ring(a RingElement, b RingElement) RingElement {
|
|
mut s := RingElement{}
|
|
for i in 0 .. n {
|
|
s[i] = field_add(a[i], b[i])
|
|
}
|
|
return s
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn poly_add_ntt(a NttElement, b NttElement) NttElement {
|
|
mut s := NttElement{}
|
|
for i in 0 .. n {
|
|
s[i] = field_add(a[i], b[i])
|
|
}
|
|
return s
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn poly_sub_ring(a RingElement, b RingElement) RingElement {
|
|
mut s := RingElement{}
|
|
for i in 0 .. n {
|
|
s[i] = field_sub(a[i], b[i])
|
|
}
|
|
return s
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn poly_sub_ntt(a NttElement, b NttElement) NttElement {
|
|
mut s := NttElement{}
|
|
for i in 0 .. n {
|
|
s[i] = field_sub(a[i], b[i])
|
|
}
|
|
return s
|
|
}
|
|
|
|
// algo. 45: MultiplyNTT
|
|
@[direct_array_access]
|
|
fn ntt_mul(a NttElement, b NttElement) NttElement {
|
|
mut p := NttElement{}
|
|
for i in 0 .. n {
|
|
p[i] = field_montgomery_mul(a[i], b[i])
|
|
}
|
|
return p
|
|
}
|
|
|
|
@[direct_array_access]
|
|
fn coefficients_exceed_bound(w RingElement, bound u32) bool {
|
|
for i in 0 .. n {
|
|
if field_infinity_norm(w[i]) >= bound {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fn ct_select_leq(a i32, b i32, yes i32, no i32) i32 {
|
|
return if subtle.constant_time_less_or_eq(int(a), int(b)) == 1 { yes } else { no }
|
|
}
|
|
|
|
fn ct_select_eq(a u32, b u32, yes u32, no u32) u32 {
|
|
return u32(subtle.constant_time_select(subtle.constant_time_eq(int(a), int(b)), int(yes),
|
|
int(no)))
|
|
}
|
|
|
|
fn ct_abs(x i32) u32 {
|
|
return u32(ct_select_leq(0, x, x, -x))
|
|
}
|