mirror of
https://github.com/emmansun/gmsm.git
synced 2025-06-28 08:23:26 +08:00
mldsa: improve sign/verify performance
This commit is contained in:
parent
b218e76328
commit
5084ea06e3
123
mldsa/mldsa44.go
123
mldsa/mldsa44.go
@ -21,6 +21,7 @@ import (
|
|||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -108,7 +109,25 @@ type PrivateKey44 struct {
|
|||||||
s1 [l44]ringElement // private secret of size L with short coefficients (-4..4) or (-2..2)
|
s1 [l44]ringElement // private secret of size L with short coefficients (-4..4) or (-2..2)
|
||||||
s2 [k44]ringElement // private secret of size K with short coefficients (-4..4) or (-2..2)
|
s2 [k44]ringElement // private secret of size K with short coefficients (-4..4) or (-2..2)
|
||||||
t0 [k44]ringElement // the Polynomial encoding of the 13 LSB of each coefficient of the uncompressed public key polynomial t. This is saved as part of the private key.
|
t0 [k44]ringElement // the Polynomial encoding of the 13 LSB of each coefficient of the uncompressed public key polynomial t. This is saved as part of the private key.
|
||||||
|
s1NTTCache [l44]nttElement
|
||||||
|
s2NTTCache [k44]nttElement
|
||||||
|
t0NTTCache [k44]nttElement
|
||||||
a [k44 * l44]nttElement // a is generated and stored in NTT representation
|
a [k44 * l44]nttElement // a is generated and stored in NTT representation
|
||||||
|
nttOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sk *PrivateKey44) ensureNTT() {
|
||||||
|
sk.nttOnce.Do(func() {
|
||||||
|
for i := range sk.s1NTTCache {
|
||||||
|
sk.s1NTTCache[i] = ntt(sk.s1[i])
|
||||||
|
}
|
||||||
|
for i := range sk.s2NTTCache {
|
||||||
|
sk.s2NTTCache[i] = ntt(sk.s2[i])
|
||||||
|
}
|
||||||
|
for i := range sk.t0NTTCache {
|
||||||
|
sk.t0NTTCache[i] = ntt(sk.t0[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Key44 is the key pair for the ML-DSA-44 signature scheme.
|
// A Key44 is the key pair for the ML-DSA-44 signature scheme.
|
||||||
@ -123,7 +142,9 @@ type PublicKey44 struct {
|
|||||||
rho [32]byte
|
rho [32]byte
|
||||||
t1 [k44]ringElement
|
t1 [k44]ringElement
|
||||||
tr [64]byte // H(pk, 64), need to further check if public key requires it
|
tr [64]byte // H(pk, 64), need to further check if public key requires it
|
||||||
|
tNTTCache [k44]nttElement
|
||||||
a [k44 * l44]nttElement // a is generated and stored in NTT representation
|
a [k44 * l44]nttElement // a is generated and stored in NTT representation
|
||||||
|
nttOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey generates and returns the corresponding public key for the given
|
// PublicKey generates and returns the corresponding public key for the given
|
||||||
@ -161,6 +182,18 @@ func (pk *PublicKey44) bytes(b []byte) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pk *PublicKey44) ensureNTT() {
|
||||||
|
pk.nttOnce.Do(func() {
|
||||||
|
t := pk.t1
|
||||||
|
for i := range k44 {
|
||||||
|
for j := range t[i] {
|
||||||
|
t[i][j] <<= d
|
||||||
|
}
|
||||||
|
pk.tNTTCache[i] = ntt(t[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Bytes returns the byte representation of the PrivateKey44.
|
// Bytes returns the byte representation of the PrivateKey44.
|
||||||
// It copies the internal seed (xi) into a fixed-size byte array
|
// It copies the internal seed (xi) into a fixed-size byte array
|
||||||
// and returns it as a slice.
|
// and returns it as a slice.
|
||||||
@ -456,18 +489,6 @@ func (sk *PrivateKey44) SignWithPreHash(rand io.Reader, message, context []byte,
|
|||||||
|
|
||||||
// See FIPS 204, Algorithm 7 ML-DSA.Sign_internal()
|
// See FIPS 204, Algorithm 7 ML-DSA.Sign_internal()
|
||||||
func (sk *PrivateKey44) signInternal(seed, mu []byte) ([]byte, error) {
|
func (sk *PrivateKey44) signInternal(seed, mu []byte) ([]byte, error) {
|
||||||
var s1NTT [l44]nttElement
|
|
||||||
var s2NTT [k44]nttElement
|
|
||||||
var t0NTT [k44]nttElement
|
|
||||||
for i := range s1NTT {
|
|
||||||
s1NTT[i] = ntt(sk.s1[i])
|
|
||||||
}
|
|
||||||
for i := range s2NTT {
|
|
||||||
s2NTT[i] = ntt(sk.s2[i])
|
|
||||||
}
|
|
||||||
for i := range t0NTT {
|
|
||||||
t0NTT[i] = ntt(sk.t0[i])
|
|
||||||
}
|
|
||||||
var rho2 [64 + 2]byte
|
var rho2 [64 + 2]byte
|
||||||
H := sha3.NewSHAKE256()
|
H := sha3.NewSHAKE256()
|
||||||
H.Write(sk.k[:])
|
H.Write(sk.k[:])
|
||||||
@ -476,22 +497,35 @@ func (sk *PrivateKey44) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
H.Read(rho2[:64])
|
H.Read(rho2[:64])
|
||||||
A := &sk.a
|
A := &sk.a
|
||||||
|
|
||||||
|
sk.ensureNTT()
|
||||||
|
zNormThreshold := int(gamma1TwoPower17 - beta44)
|
||||||
|
r0NormThreshold := int(gamma2QMinus1Div88 - beta44)
|
||||||
|
|
||||||
// rejection sampling loop
|
// rejection sampling loop
|
||||||
for kappa := 0; ; kappa = kappa + l44 {
|
for kappa := 0; ; kappa = kappa + l44 {
|
||||||
// expand mask
|
// expand mask
|
||||||
var y [l44]ringElement
|
var (
|
||||||
|
y [l44]ringElement
|
||||||
|
yNTT [l44]nttElement
|
||||||
|
)
|
||||||
for i := range l44 {
|
for i := range l44 {
|
||||||
index := kappa + i
|
index := kappa + i
|
||||||
rho2[64] = byte(index)
|
rho2[64] = byte(index)
|
||||||
rho2[65] = byte(index >> 8)
|
rho2[65] = byte(index >> 8)
|
||||||
y[i] = expandMask(rho2[:], gamma1TwoPower17)
|
y[i] = expandMask(rho2[:], gamma1TwoPower17)
|
||||||
}
|
}
|
||||||
|
// compute y in NTT form
|
||||||
|
for i := range l44 {
|
||||||
|
yNTT[i] = ntt(y[i])
|
||||||
|
}
|
||||||
// compute w and w1
|
// compute w and w1
|
||||||
var w, w1 [k44]ringElement
|
var (
|
||||||
var wNTT [k44]nttElement
|
w, w1 [k44]ringElement
|
||||||
|
wNTT [k44]nttElement
|
||||||
|
)
|
||||||
for i := range w {
|
for i := range w {
|
||||||
for j := range y {
|
for j := range yNTT {
|
||||||
wNTT[i] = polyAdd(wNTT[i], nttMul(ntt(y[j]), A[i*l44+j]))
|
wNTT[i] = polyAdd(wNTT[i], nttMul(yNTT[j], A[i*l44+j]))
|
||||||
}
|
}
|
||||||
w[i] = inverseNTT(wNTT[i])
|
w[i] = inverseNTT(wNTT[i])
|
||||||
// high bits
|
// high bits
|
||||||
@ -500,8 +534,10 @@ func (sk *PrivateKey44) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// commitment hash
|
// commitment hash
|
||||||
var cTilde [lambda128 / 4]byte
|
var (
|
||||||
var w1Encoded [encodingSize6]byte
|
cTilde [lambda128 / 4]byte
|
||||||
|
w1Encoded [encodingSize6]byte
|
||||||
|
)
|
||||||
H.Reset()
|
H.Reset()
|
||||||
H.Write(mu[:])
|
H.Write(mu[:])
|
||||||
for i := range k44 {
|
for i := range k44 {
|
||||||
@ -512,18 +548,20 @@ func (sk *PrivateKey44) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
// verifier's challenge
|
// verifier's challenge
|
||||||
cNTT := ntt(sampleInBall(cTilde[:], tau39))
|
cNTT := ntt(sampleInBall(cTilde[:], tau39))
|
||||||
|
|
||||||
var cs1 [l44]ringElement
|
var (
|
||||||
var cs2 [k44]ringElement
|
cs1 [l44]ringElement
|
||||||
var z [l44]ringElement
|
cs2 [k44]ringElement
|
||||||
var r0 [k44][n]int32
|
z [l44]ringElement
|
||||||
|
r0 [k44][n]int32
|
||||||
|
)
|
||||||
// compute <<cs1>> and z = <<cs1>> + y
|
// compute <<cs1>> and z = <<cs1>> + y
|
||||||
for i := range l44 {
|
for i := range l44 {
|
||||||
cs1[i] = inverseNTT(nttMul(cNTT, s1NTT[i]))
|
cs1[i] = inverseNTT(nttMul(cNTT, sk.s1NTTCache[i]))
|
||||||
z[i] = polyAdd(cs1[i], y[i])
|
z[i] = polyAdd(cs1[i], y[i])
|
||||||
}
|
}
|
||||||
// compute <<cs2>> and r0 = LowBits(w - <<cs2>>)
|
// compute <<cs2>> and r0 = LowBits(w - <<cs2>>)
|
||||||
for i := range k44 {
|
for i := range k44 {
|
||||||
cs2[i] = inverseNTT(nttMul(cNTT, s2NTT[i]))
|
cs2[i] = inverseNTT(nttMul(cNTT, sk.s2NTTCache[i]))
|
||||||
for j := range cs2[i] {
|
for j := range cs2[i] {
|
||||||
_, r0[i][j] = decompose(fieldSub(w[i][j], cs2[i][j]), gamma2QMinus1Div88)
|
_, r0[i][j] = decompose(fieldSub(w[i][j], cs2[i][j]), gamma2QMinus1Div88)
|
||||||
}
|
}
|
||||||
@ -532,13 +570,13 @@ func (sk *PrivateKey44) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
r0Norm := vectorInfinityNormSigned(r0[:], 0)
|
r0Norm := vectorInfinityNormSigned(r0[:], 0)
|
||||||
|
|
||||||
// if zNorm >= gamma1 - beta || r0Norm >= gamma2 - beta, then continue
|
// if zNorm >= gamma1 - beta || r0Norm >= gamma2 - beta, then continue
|
||||||
if subtle.ConstantTimeLessOrEq(int(gamma1TwoPower17-beta44), zNorm)|subtle.ConstantTimeLessOrEq(int(gamma2QMinus1Div88-beta44), r0Norm) == 1 {
|
if subtle.ConstantTimeLessOrEq(zNormThreshold, zNorm)|subtle.ConstantTimeLessOrEq(r0NormThreshold, r0Norm) == 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// compute <<ct0>>
|
// compute <<ct0>>
|
||||||
var ct0 [k44]ringElement
|
var ct0 [k44]ringElement
|
||||||
for i := range k44 {
|
for i := range k44 {
|
||||||
ct0[i] = inverseNTT(nttMul(cNTT, t0NTT[i]))
|
ct0[i] = inverseNTT(nttMul(cNTT, sk.t0NTTCache[i]))
|
||||||
}
|
}
|
||||||
// compute infinity norm of <<ct0>>
|
// compute infinity norm of <<ct0>>
|
||||||
ct0Norm := vectorInfinityNorm(ct0[:], 0)
|
ct0Norm := vectorInfinityNorm(ct0[:], 0)
|
||||||
@ -618,9 +656,14 @@ func (pk *PublicKey44) verifyInternal(sig, mu []byte) bool {
|
|||||||
// Decode the signature
|
// Decode the signature
|
||||||
cTilde := sig[:lambda128/4]
|
cTilde := sig[:lambda128/4]
|
||||||
sig = sig[lambda128/4:]
|
sig = sig[lambda128/4:]
|
||||||
var z [l44]ringElement
|
|
||||||
|
var (
|
||||||
|
z [l44]ringElement
|
||||||
|
zNTT [l44]nttElement
|
||||||
|
)
|
||||||
for i := range l44 {
|
for i := range l44 {
|
||||||
bitUnpackSignedTwoPower17(sig, &z[i])
|
bitUnpackSignedTwoPower17(sig, &z[i])
|
||||||
|
zNTT[i] = ntt(z[i])
|
||||||
sig = sig[encodingSize18:]
|
sig = sig[encodingSize18:]
|
||||||
}
|
}
|
||||||
zNorm := vectorInfinityNorm(z[:], 0)
|
zNorm := vectorInfinityNorm(z[:], 0)
|
||||||
@ -631,25 +674,23 @@ func (pk *PublicKey44) verifyInternal(sig, mu []byte) bool {
|
|||||||
// verifier's challenge
|
// verifier's challenge
|
||||||
cNTT := ntt(sampleInBall(cTilde[:], tau39))
|
cNTT := ntt(sampleInBall(cTilde[:], tau39))
|
||||||
|
|
||||||
// t = t1 * 2^d
|
pk.ensureNTT()
|
||||||
// tNTT = NTT(t)*cNTT
|
// tNTT = tNTTCache*cNTT
|
||||||
var tNTT [k44]nttElement
|
var tNTT [k44]nttElement
|
||||||
t := pk.t1
|
|
||||||
for i := range k44 {
|
for i := range k44 {
|
||||||
for j := range t[i] {
|
tNTT[i] = nttMul(pk.tNTTCache[i], cNTT)
|
||||||
t[i][j] <<= d
|
|
||||||
}
|
|
||||||
tNTT[i] = nttMul(ntt(t[i]), cNTT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var w1, wApprox [k44]ringElement
|
var (
|
||||||
var zNTT [k44]nttElement
|
w1, wApprox [k44]ringElement
|
||||||
|
zNTTMulA [k44]nttElement
|
||||||
|
)
|
||||||
for i := range k44 {
|
for i := range k44 {
|
||||||
for j := 0; j < l44; j++ {
|
for j := range l44 {
|
||||||
zNTT[i] = polyAdd(zNTT[i], nttMul(ntt(z[j]), pk.a[i*l44+j]))
|
zNTTMulA[i] = polyAdd(zNTTMulA[i], nttMul(zNTT[j], pk.a[i*l44+j]))
|
||||||
}
|
}
|
||||||
zNTT[i] = polySub(zNTT[i], tNTT[i])
|
zNTTMulA[i] = polySub(zNTTMulA[i], tNTT[i])
|
||||||
wApprox[i] = inverseNTT(zNTT[i])
|
wApprox[i] = inverseNTT(zNTTMulA[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
H := sha3.NewSHAKE256()
|
H := sha3.NewSHAKE256()
|
||||||
|
123
mldsa/mldsa65.go
123
mldsa/mldsa65.go
@ -14,6 +14,7 @@ import (
|
|||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A PrivateKey65 is the private key for the ML-DSA-65 signature scheme.
|
// A PrivateKey65 is the private key for the ML-DSA-65 signature scheme.
|
||||||
@ -24,7 +25,25 @@ type PrivateKey65 struct {
|
|||||||
s1 [l65]ringElement // private secret of size L with short coefficients (-4..4) or (-2..2)
|
s1 [l65]ringElement // private secret of size L with short coefficients (-4..4) or (-2..2)
|
||||||
s2 [k65]ringElement // private secret of size K with short coefficients (-4..4) or (-2..2)
|
s2 [k65]ringElement // private secret of size K with short coefficients (-4..4) or (-2..2)
|
||||||
t0 [k65]ringElement // the Polynomial encoding of the 13 LSB of each coefficient of the uncompressed public key polynomial t. This is saved as part of the private key.
|
t0 [k65]ringElement // the Polynomial encoding of the 13 LSB of each coefficient of the uncompressed public key polynomial t. This is saved as part of the private key.
|
||||||
|
s1NTTCache [l65]nttElement
|
||||||
|
s2NTTCache [k65]nttElement
|
||||||
|
t0NTTCache [k65]nttElement
|
||||||
a [k65 * l65]nttElement // a is generated and stored in NTT representation
|
a [k65 * l65]nttElement // a is generated and stored in NTT representation
|
||||||
|
nttOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sk *PrivateKey65) ensureNTT() {
|
||||||
|
sk.nttOnce.Do(func() {
|
||||||
|
for i := range sk.s1NTTCache {
|
||||||
|
sk.s1NTTCache[i] = ntt(sk.s1[i])
|
||||||
|
}
|
||||||
|
for i := range sk.s2NTTCache {
|
||||||
|
sk.s2NTTCache[i] = ntt(sk.s2[i])
|
||||||
|
}
|
||||||
|
for i := range sk.t0NTTCache {
|
||||||
|
sk.t0NTTCache[i] = ntt(sk.t0[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Key65 is the key pair for the ML-DSA-65 signature scheme.
|
// A Key65 is the key pair for the ML-DSA-65 signature scheme.
|
||||||
@ -39,7 +58,9 @@ type PublicKey65 struct {
|
|||||||
rho [32]byte
|
rho [32]byte
|
||||||
t1 [k65]ringElement
|
t1 [k65]ringElement
|
||||||
tr [64]byte // H(pk, 64), need to further check if public key requires it
|
tr [64]byte // H(pk, 64), need to further check if public key requires it
|
||||||
|
tNTTCache [k65]nttElement
|
||||||
a [k65 * l65]nttElement // a is generated and stored in NTT representation
|
a [k65 * l65]nttElement // a is generated and stored in NTT representation
|
||||||
|
nttOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey generates and returns the corresponding public key for the given
|
// PublicKey generates and returns the corresponding public key for the given
|
||||||
@ -77,6 +98,18 @@ func (pk *PublicKey65) bytes(b []byte) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pk *PublicKey65) ensureNTT() {
|
||||||
|
pk.nttOnce.Do(func() {
|
||||||
|
t := pk.t1
|
||||||
|
for i := range k65 {
|
||||||
|
for j := range t[i] {
|
||||||
|
t[i][j] <<= d
|
||||||
|
}
|
||||||
|
pk.tNTTCache[i] = ntt(t[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Bytes returns the byte representation of the PrivateKey65.
|
// Bytes returns the byte representation of the PrivateKey65.
|
||||||
// It copies the internal seed (xi) into a fixed-size byte array
|
// It copies the internal seed (xi) into a fixed-size byte array
|
||||||
// and returns it as a slice.
|
// and returns it as a slice.
|
||||||
@ -372,18 +405,6 @@ func (sk *PrivateKey65) SignWithPreHash(rand io.Reader, message, context []byte,
|
|||||||
|
|
||||||
// See FIPS 204, Algorithm 7 ML-DSA.Sign_internal()
|
// See FIPS 204, Algorithm 7 ML-DSA.Sign_internal()
|
||||||
func (sk *PrivateKey65) signInternal(seed, mu []byte) ([]byte, error) {
|
func (sk *PrivateKey65) signInternal(seed, mu []byte) ([]byte, error) {
|
||||||
var s1NTT [l65]nttElement
|
|
||||||
var s2NTT [k65]nttElement
|
|
||||||
var t0NTT [k65]nttElement
|
|
||||||
for i := range s1NTT {
|
|
||||||
s1NTT[i] = ntt(sk.s1[i])
|
|
||||||
}
|
|
||||||
for i := range s2NTT {
|
|
||||||
s2NTT[i] = ntt(sk.s2[i])
|
|
||||||
}
|
|
||||||
for i := range t0NTT {
|
|
||||||
t0NTT[i] = ntt(sk.t0[i])
|
|
||||||
}
|
|
||||||
var rho2 [64 + 2]byte
|
var rho2 [64 + 2]byte
|
||||||
H := sha3.NewSHAKE256()
|
H := sha3.NewSHAKE256()
|
||||||
H.Write(sk.k[:])
|
H.Write(sk.k[:])
|
||||||
@ -392,22 +413,35 @@ func (sk *PrivateKey65) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
H.Read(rho2[:64])
|
H.Read(rho2[:64])
|
||||||
A := &sk.a
|
A := &sk.a
|
||||||
|
|
||||||
|
sk.ensureNTT()
|
||||||
|
zNormThreshold := int(gamma1TwoPower19 - beta65)
|
||||||
|
r0NormThreshold := int(gamma2QMinus1Div32 - beta65)
|
||||||
|
|
||||||
// rejection sampling loop
|
// rejection sampling loop
|
||||||
for kappa := 0; ; kappa = kappa + l65 {
|
for kappa := 0; ; kappa = kappa + l65 {
|
||||||
// expand mask
|
// expand mask
|
||||||
var y [l65]ringElement
|
var (
|
||||||
|
y [l65]ringElement
|
||||||
|
yNTT [l65]nttElement
|
||||||
|
)
|
||||||
for i := range l65 {
|
for i := range l65 {
|
||||||
index := kappa + i
|
index := kappa + i
|
||||||
rho2[64] = byte(index)
|
rho2[64] = byte(index)
|
||||||
rho2[65] = byte(index >> 8)
|
rho2[65] = byte(index >> 8)
|
||||||
y[i] = expandMask(rho2[:], gamma1TwoPower19)
|
y[i] = expandMask(rho2[:], gamma1TwoPower19)
|
||||||
}
|
}
|
||||||
|
// compute y in NTT form
|
||||||
|
for i := range l65 {
|
||||||
|
yNTT[i] = ntt(y[i])
|
||||||
|
}
|
||||||
// compute w and w1
|
// compute w and w1
|
||||||
var w, w1 [k65]ringElement
|
var (
|
||||||
var wNTT [k65]nttElement
|
w, w1 [k65]ringElement
|
||||||
|
wNTT [k65]nttElement
|
||||||
|
)
|
||||||
for i := range w {
|
for i := range w {
|
||||||
for j := range y {
|
for j := range yNTT {
|
||||||
wNTT[i] = polyAdd(wNTT[i], nttMul(ntt(y[j]), A[i*l65+j]))
|
wNTT[i] = polyAdd(wNTT[i], nttMul(yNTT[j], A[i*l65+j]))
|
||||||
}
|
}
|
||||||
w[i] = inverseNTT(wNTT[i])
|
w[i] = inverseNTT(wNTT[i])
|
||||||
// high bits
|
// high bits
|
||||||
@ -416,8 +450,10 @@ func (sk *PrivateKey65) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// commitment hash
|
// commitment hash
|
||||||
var cTilde [lambda192 / 4]byte
|
var (
|
||||||
var w1Encoded [encodingSize4]byte
|
cTilde [lambda192 / 4]byte
|
||||||
|
w1Encoded [encodingSize4]byte
|
||||||
|
)
|
||||||
H.Reset()
|
H.Reset()
|
||||||
H.Write(mu[:])
|
H.Write(mu[:])
|
||||||
for i := range k65 {
|
for i := range k65 {
|
||||||
@ -428,18 +464,20 @@ func (sk *PrivateKey65) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
// verifier's challenge
|
// verifier's challenge
|
||||||
cNTT := ntt(sampleInBall(cTilde[:], tau49))
|
cNTT := ntt(sampleInBall(cTilde[:], tau49))
|
||||||
|
|
||||||
var cs1 [l65]ringElement
|
var (
|
||||||
var cs2 [k65]ringElement
|
cs1 [l65]ringElement
|
||||||
var z [l65]ringElement
|
cs2 [k65]ringElement
|
||||||
var r0 [k65][n]int32
|
z [l65]ringElement
|
||||||
|
r0 [k65][n]int32
|
||||||
|
)
|
||||||
// compute <<cs1>> and z = <<cs1>> + y
|
// compute <<cs1>> and z = <<cs1>> + y
|
||||||
for i := range l65 {
|
for i := range l65 {
|
||||||
cs1[i] = inverseNTT(nttMul(cNTT, s1NTT[i]))
|
cs1[i] = inverseNTT(nttMul(cNTT, sk.s1NTTCache[i]))
|
||||||
z[i] = polyAdd(cs1[i], y[i])
|
z[i] = polyAdd(cs1[i], y[i])
|
||||||
}
|
}
|
||||||
// compute <<cs2>> and r0 = LowBits(w - <<cs2>>)
|
// compute <<cs2>> and r0 = LowBits(w - <<cs2>>)
|
||||||
for i := range k65 {
|
for i := range k65 {
|
||||||
cs2[i] = inverseNTT(nttMul(cNTT, s2NTT[i]))
|
cs2[i] = inverseNTT(nttMul(cNTT, sk.s2NTTCache[i]))
|
||||||
for j := range cs2[i] {
|
for j := range cs2[i] {
|
||||||
_, r0[i][j] = decompose(fieldSub(w[i][j], cs2[i][j]), gamma2QMinus1Div32)
|
_, r0[i][j] = decompose(fieldSub(w[i][j], cs2[i][j]), gamma2QMinus1Div32)
|
||||||
}
|
}
|
||||||
@ -448,13 +486,13 @@ func (sk *PrivateKey65) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
r0Norm := vectorInfinityNormSigned(r0[:], 0)
|
r0Norm := vectorInfinityNormSigned(r0[:], 0)
|
||||||
|
|
||||||
// if zNorm >= gamma1 - beta || r0Norm >= gamma2 - beta, then continue
|
// if zNorm >= gamma1 - beta || r0Norm >= gamma2 - beta, then continue
|
||||||
if subtle.ConstantTimeLessOrEq(int(gamma1TwoPower19-beta65), zNorm)|subtle.ConstantTimeLessOrEq(int(gamma2QMinus1Div32-beta65), r0Norm) == 1 {
|
if subtle.ConstantTimeLessOrEq(zNormThreshold, zNorm)|subtle.ConstantTimeLessOrEq(r0NormThreshold, r0Norm) == 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// compute <<ct0>>
|
// compute <<ct0>>
|
||||||
var ct0 [k65]ringElement
|
var ct0 [k65]ringElement
|
||||||
for i := range k65 {
|
for i := range k65 {
|
||||||
ct0[i] = inverseNTT(nttMul(cNTT, t0NTT[i]))
|
ct0[i] = inverseNTT(nttMul(cNTT, sk.t0NTTCache[i]))
|
||||||
}
|
}
|
||||||
// compute infinity norm of <<ct0>>
|
// compute infinity norm of <<ct0>>
|
||||||
ct0Norm := vectorInfinityNorm(ct0[:], 0)
|
ct0Norm := vectorInfinityNorm(ct0[:], 0)
|
||||||
@ -534,9 +572,14 @@ func (pk *PublicKey65) verifyInternal(sig, mu []byte) bool {
|
|||||||
// Decode the signature
|
// Decode the signature
|
||||||
cTilde := sig[:lambda192/4]
|
cTilde := sig[:lambda192/4]
|
||||||
sig = sig[lambda192/4:]
|
sig = sig[lambda192/4:]
|
||||||
var z [l65]ringElement
|
|
||||||
|
var (
|
||||||
|
z [l65]ringElement
|
||||||
|
zNTT [l65]nttElement
|
||||||
|
)
|
||||||
for i := range l65 {
|
for i := range l65 {
|
||||||
bitUnpackSignedTwoPower19(sig, &z[i])
|
bitUnpackSignedTwoPower19(sig, &z[i])
|
||||||
|
zNTT[i] = ntt(z[i])
|
||||||
sig = sig[encodingSize20:]
|
sig = sig[encodingSize20:]
|
||||||
}
|
}
|
||||||
zNorm := vectorInfinityNorm(z[:], 0)
|
zNorm := vectorInfinityNorm(z[:], 0)
|
||||||
@ -547,25 +590,23 @@ func (pk *PublicKey65) verifyInternal(sig, mu []byte) bool {
|
|||||||
// verifier's challenge
|
// verifier's challenge
|
||||||
cNTT := ntt(sampleInBall(cTilde[:], tau49))
|
cNTT := ntt(sampleInBall(cTilde[:], tau49))
|
||||||
|
|
||||||
// t = t1 * 2^d
|
pk.ensureNTT()
|
||||||
// tNTT = NTT(t)*cNTT
|
// tNTT = tNTTCache*cNTT
|
||||||
var tNTT [k65]nttElement
|
var tNTT [k65]nttElement
|
||||||
t := pk.t1
|
|
||||||
for i := range k65 {
|
for i := range k65 {
|
||||||
for j := range t[i] {
|
tNTT[i] = nttMul(pk.tNTTCache[i], cNTT)
|
||||||
t[i][j] <<= d
|
|
||||||
}
|
|
||||||
tNTT[i] = nttMul(ntt(t[i]), cNTT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var w1, wApprox [k65]ringElement
|
var (
|
||||||
var zNTT [k65]nttElement
|
w1, wApprox [k65]ringElement
|
||||||
|
zNTTMulA [k65]nttElement
|
||||||
|
)
|
||||||
for i := range k65 {
|
for i := range k65 {
|
||||||
for j := 0; j < l65; j++ {
|
for j := range l65 {
|
||||||
zNTT[i] = polyAdd(zNTT[i], nttMul(ntt(z[j]), pk.a[i*l65+j]))
|
zNTTMulA[i] = polyAdd(zNTTMulA[i], nttMul(zNTT[j], pk.a[i*l65+j]))
|
||||||
}
|
}
|
||||||
zNTT[i] = polySub(zNTT[i], tNTT[i])
|
zNTTMulA[i] = polySub(zNTTMulA[i], tNTT[i])
|
||||||
wApprox[i] = inverseNTT(zNTT[i])
|
wApprox[i] = inverseNTT(zNTTMulA[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
H := sha3.NewSHAKE256()
|
H := sha3.NewSHAKE256()
|
||||||
|
123
mldsa/mldsa87.go
123
mldsa/mldsa87.go
@ -14,6 +14,7 @@ import (
|
|||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A PrivateKey87 is the private key for the ML-DSA-87 signature scheme.
|
// A PrivateKey87 is the private key for the ML-DSA-87 signature scheme.
|
||||||
@ -24,7 +25,25 @@ type PrivateKey87 struct {
|
|||||||
s1 [l87]ringElement // private secret of size L with short coefficients (-4..4) or (-2..2)
|
s1 [l87]ringElement // private secret of size L with short coefficients (-4..4) or (-2..2)
|
||||||
s2 [k87]ringElement // private secret of size K with short coefficients (-4..4) or (-2..2)
|
s2 [k87]ringElement // private secret of size K with short coefficients (-4..4) or (-2..2)
|
||||||
t0 [k87]ringElement // the Polynomial encoding of the 13 LSB of each coefficient of the uncompressed public key polynomial t. This is saved as part of the private key.
|
t0 [k87]ringElement // the Polynomial encoding of the 13 LSB of each coefficient of the uncompressed public key polynomial t. This is saved as part of the private key.
|
||||||
|
s1NTTCache [l87]nttElement
|
||||||
|
s2NTTCache [k87]nttElement
|
||||||
|
t0NTTCache [k87]nttElement
|
||||||
a [k87 * l87]nttElement // a is generated and stored in NTT representation
|
a [k87 * l87]nttElement // a is generated and stored in NTT representation
|
||||||
|
nttOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sk *PrivateKey87) ensureNTT() {
|
||||||
|
sk.nttOnce.Do(func() {
|
||||||
|
for i := range sk.s1NTTCache {
|
||||||
|
sk.s1NTTCache[i] = ntt(sk.s1[i])
|
||||||
|
}
|
||||||
|
for i := range sk.s2NTTCache {
|
||||||
|
sk.s2NTTCache[i] = ntt(sk.s2[i])
|
||||||
|
}
|
||||||
|
for i := range sk.t0NTTCache {
|
||||||
|
sk.t0NTTCache[i] = ntt(sk.t0[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Key87 is the key pair for the ML-DSA-87 signature scheme.
|
// A Key87 is the key pair for the ML-DSA-87 signature scheme.
|
||||||
@ -39,7 +58,9 @@ type PublicKey87 struct {
|
|||||||
rho [32]byte
|
rho [32]byte
|
||||||
t1 [k87]ringElement
|
t1 [k87]ringElement
|
||||||
tr [64]byte // H(pk, 64), need to further check if public key requires it
|
tr [64]byte // H(pk, 64), need to further check if public key requires it
|
||||||
|
tNTTCache [k87]nttElement
|
||||||
a [k87 * l87]nttElement // a is generated and stored in NTT representation
|
a [k87 * l87]nttElement // a is generated and stored in NTT representation
|
||||||
|
nttOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey generates and returns the corresponding public key for the given
|
// PublicKey generates and returns the corresponding public key for the given
|
||||||
@ -77,6 +98,18 @@ func (pk *PublicKey87) bytes(b []byte) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pk *PublicKey87) ensureNTT() {
|
||||||
|
pk.nttOnce.Do(func() {
|
||||||
|
t := pk.t1
|
||||||
|
for i := range k87 {
|
||||||
|
for j := range t[i] {
|
||||||
|
t[i][j] <<= d
|
||||||
|
}
|
||||||
|
pk.tNTTCache[i] = ntt(t[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Bytes returns the byte representation of the PrivateKey87.
|
// Bytes returns the byte representation of the PrivateKey87.
|
||||||
// It copies the internal seed (xi) into a fixed-size byte array
|
// It copies the internal seed (xi) into a fixed-size byte array
|
||||||
// and returns it as a slice.
|
// and returns it as a slice.
|
||||||
@ -372,18 +405,6 @@ func (sk *PrivateKey87) SignWithPreHash(rand io.Reader, message, context []byte,
|
|||||||
|
|
||||||
// See FIPS 204, Algorithm 7 ML-DSA.Sign_internal()
|
// See FIPS 204, Algorithm 7 ML-DSA.Sign_internal()
|
||||||
func (sk *PrivateKey87) signInternal(seed, mu []byte) ([]byte, error) {
|
func (sk *PrivateKey87) signInternal(seed, mu []byte) ([]byte, error) {
|
||||||
var s1NTT [l87]nttElement
|
|
||||||
var s2NTT [k87]nttElement
|
|
||||||
var t0NTT [k87]nttElement
|
|
||||||
for i := range s1NTT {
|
|
||||||
s1NTT[i] = ntt(sk.s1[i])
|
|
||||||
}
|
|
||||||
for i := range s2NTT {
|
|
||||||
s2NTT[i] = ntt(sk.s2[i])
|
|
||||||
}
|
|
||||||
for i := range t0NTT {
|
|
||||||
t0NTT[i] = ntt(sk.t0[i])
|
|
||||||
}
|
|
||||||
var rho2 [64 + 2]byte
|
var rho2 [64 + 2]byte
|
||||||
H := sha3.NewSHAKE256()
|
H := sha3.NewSHAKE256()
|
||||||
H.Write(sk.k[:])
|
H.Write(sk.k[:])
|
||||||
@ -392,22 +413,35 @@ func (sk *PrivateKey87) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
H.Read(rho2[:64])
|
H.Read(rho2[:64])
|
||||||
A := &sk.a
|
A := &sk.a
|
||||||
|
|
||||||
|
sk.ensureNTT()
|
||||||
|
zNormThreshold := int(gamma1TwoPower19 - beta87)
|
||||||
|
r0NormThreshold := int(gamma2QMinus1Div32 - beta87)
|
||||||
|
|
||||||
// rejection sampling loop
|
// rejection sampling loop
|
||||||
for kappa := 0; ; kappa = kappa + l87 {
|
for kappa := 0; ; kappa = kappa + l87 {
|
||||||
// expand mask
|
// expand mask
|
||||||
var y [l87]ringElement
|
var (
|
||||||
|
y [l87]ringElement
|
||||||
|
yNTT [l87]nttElement
|
||||||
|
)
|
||||||
for i := range l87 {
|
for i := range l87 {
|
||||||
index := kappa + i
|
index := kappa + i
|
||||||
rho2[64] = byte(index)
|
rho2[64] = byte(index)
|
||||||
rho2[65] = byte(index >> 8)
|
rho2[65] = byte(index >> 8)
|
||||||
y[i] = expandMask(rho2[:], gamma1TwoPower19)
|
y[i] = expandMask(rho2[:], gamma1TwoPower19)
|
||||||
}
|
}
|
||||||
|
// compute y in NTT form
|
||||||
|
for i := range l87 {
|
||||||
|
yNTT[i] = ntt(y[i])
|
||||||
|
}
|
||||||
// compute w and w1
|
// compute w and w1
|
||||||
var w, w1 [k87]ringElement
|
var (
|
||||||
var wNTT [k87]nttElement
|
w, w1 [k87]ringElement
|
||||||
|
wNTT [k87]nttElement
|
||||||
|
)
|
||||||
for i := range w {
|
for i := range w {
|
||||||
for j := range y {
|
for j := range yNTT {
|
||||||
wNTT[i] = polyAdd(wNTT[i], nttMul(ntt(y[j]), A[i*l87+j]))
|
wNTT[i] = polyAdd(wNTT[i], nttMul(yNTT[j], A[i*l87+j]))
|
||||||
}
|
}
|
||||||
w[i] = inverseNTT(wNTT[i])
|
w[i] = inverseNTT(wNTT[i])
|
||||||
// high bits
|
// high bits
|
||||||
@ -416,8 +450,10 @@ func (sk *PrivateKey87) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// commitment hash
|
// commitment hash
|
||||||
var cTilde [lambda256 / 4]byte
|
var (
|
||||||
var w1Encoded [encodingSize4]byte
|
cTilde [lambda256 / 4]byte
|
||||||
|
w1Encoded [encodingSize4]byte
|
||||||
|
)
|
||||||
H.Reset()
|
H.Reset()
|
||||||
H.Write(mu[:])
|
H.Write(mu[:])
|
||||||
for i := range k87 {
|
for i := range k87 {
|
||||||
@ -428,18 +464,20 @@ func (sk *PrivateKey87) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
// verifier's challenge
|
// verifier's challenge
|
||||||
cNTT := ntt(sampleInBall(cTilde[:], tau60))
|
cNTT := ntt(sampleInBall(cTilde[:], tau60))
|
||||||
|
|
||||||
var cs1 [l87]ringElement
|
var (
|
||||||
var cs2 [k87]ringElement
|
cs1 [l87]ringElement
|
||||||
var z [l87]ringElement
|
cs2 [k87]ringElement
|
||||||
var r0 [k87][n]int32
|
z [l87]ringElement
|
||||||
|
r0 [k87][n]int32
|
||||||
|
)
|
||||||
// compute <<cs1>> and z = <<cs1>> + y
|
// compute <<cs1>> and z = <<cs1>> + y
|
||||||
for i := range l87 {
|
for i := range l87 {
|
||||||
cs1[i] = inverseNTT(nttMul(cNTT, s1NTT[i]))
|
cs1[i] = inverseNTT(nttMul(cNTT, sk.s1NTTCache[i]))
|
||||||
z[i] = polyAdd(cs1[i], y[i])
|
z[i] = polyAdd(cs1[i], y[i])
|
||||||
}
|
}
|
||||||
// compute <<cs2>> and r0 = LowBits(w - <<cs2>>)
|
// compute <<cs2>> and r0 = LowBits(w - <<cs2>>)
|
||||||
for i := range k87 {
|
for i := range k87 {
|
||||||
cs2[i] = inverseNTT(nttMul(cNTT, s2NTT[i]))
|
cs2[i] = inverseNTT(nttMul(cNTT, sk.s2NTTCache[i]))
|
||||||
for j := range cs2[i] {
|
for j := range cs2[i] {
|
||||||
_, r0[i][j] = decompose(fieldSub(w[i][j], cs2[i][j]), gamma2QMinus1Div32)
|
_, r0[i][j] = decompose(fieldSub(w[i][j], cs2[i][j]), gamma2QMinus1Div32)
|
||||||
}
|
}
|
||||||
@ -448,13 +486,13 @@ func (sk *PrivateKey87) signInternal(seed, mu []byte) ([]byte, error) {
|
|||||||
r0Norm := vectorInfinityNormSigned(r0[:], 0)
|
r0Norm := vectorInfinityNormSigned(r0[:], 0)
|
||||||
|
|
||||||
// if zNorm >= gamma1 - beta || r0Norm >= gamma2 - beta, then continue
|
// if zNorm >= gamma1 - beta || r0Norm >= gamma2 - beta, then continue
|
||||||
if subtle.ConstantTimeLessOrEq(int(gamma1TwoPower19-beta87), zNorm)|subtle.ConstantTimeLessOrEq(int(gamma2QMinus1Div32-beta87), r0Norm) == 1 {
|
if subtle.ConstantTimeLessOrEq(zNormThreshold, zNorm)|subtle.ConstantTimeLessOrEq(r0NormThreshold, r0Norm) == 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// compute <<ct0>>
|
// compute <<ct0>>
|
||||||
var ct0 [k87]ringElement
|
var ct0 [k87]ringElement
|
||||||
for i := range k87 {
|
for i := range k87 {
|
||||||
ct0[i] = inverseNTT(nttMul(cNTT, t0NTT[i]))
|
ct0[i] = inverseNTT(nttMul(cNTT, sk.t0NTTCache[i]))
|
||||||
}
|
}
|
||||||
// compute infinity norm of <<ct0>>
|
// compute infinity norm of <<ct0>>
|
||||||
ct0Norm := vectorInfinityNorm(ct0[:], 0)
|
ct0Norm := vectorInfinityNorm(ct0[:], 0)
|
||||||
@ -534,9 +572,14 @@ func (pk *PublicKey87) verifyInternal(sig, mu []byte) bool {
|
|||||||
// Decode the signature
|
// Decode the signature
|
||||||
cTilde := sig[:lambda256/4]
|
cTilde := sig[:lambda256/4]
|
||||||
sig = sig[lambda256/4:]
|
sig = sig[lambda256/4:]
|
||||||
var z [l87]ringElement
|
|
||||||
|
var (
|
||||||
|
z [l87]ringElement
|
||||||
|
zNTT [l87]nttElement
|
||||||
|
)
|
||||||
for i := range l87 {
|
for i := range l87 {
|
||||||
bitUnpackSignedTwoPower19(sig, &z[i])
|
bitUnpackSignedTwoPower19(sig, &z[i])
|
||||||
|
zNTT[i] = ntt(z[i])
|
||||||
sig = sig[encodingSize20:]
|
sig = sig[encodingSize20:]
|
||||||
}
|
}
|
||||||
zNorm := vectorInfinityNorm(z[:], 0)
|
zNorm := vectorInfinityNorm(z[:], 0)
|
||||||
@ -547,25 +590,23 @@ func (pk *PublicKey87) verifyInternal(sig, mu []byte) bool {
|
|||||||
// verifier's challenge
|
// verifier's challenge
|
||||||
cNTT := ntt(sampleInBall(cTilde[:], tau60))
|
cNTT := ntt(sampleInBall(cTilde[:], tau60))
|
||||||
|
|
||||||
// t = t1 * 2^d
|
pk.ensureNTT()
|
||||||
// tNTT = NTT(t)*cNTT
|
// tNTT = tNTTCache*cNTT
|
||||||
var tNTT [k87]nttElement
|
var tNTT [k87]nttElement
|
||||||
t := pk.t1
|
|
||||||
for i := range k87 {
|
for i := range k87 {
|
||||||
for j := range t[i] {
|
tNTT[i] = nttMul(pk.tNTTCache[i], cNTT)
|
||||||
t[i][j] <<= d
|
|
||||||
}
|
|
||||||
tNTT[i] = nttMul(ntt(t[i]), cNTT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var w1, wApprox [k87]ringElement
|
var (
|
||||||
var zNTT [k87]nttElement
|
w1, wApprox [k87]ringElement
|
||||||
|
zNTTMulA [k87]nttElement
|
||||||
|
)
|
||||||
for i := range k87 {
|
for i := range k87 {
|
||||||
for j := 0; j < l87; j++ {
|
for j := range l87 {
|
||||||
zNTT[i] = polyAdd(zNTT[i], nttMul(ntt(z[j]), pk.a[i*l87+j]))
|
zNTTMulA[i] = polyAdd(zNTTMulA[i], nttMul(zNTT[j], pk.a[i*l87+j]))
|
||||||
}
|
}
|
||||||
zNTT[i] = polySub(zNTT[i], tNTT[i])
|
zNTTMulA[i] = polySub(zNTTMulA[i], tNTT[i])
|
||||||
wApprox[i] = inverseNTT(zNTT[i])
|
wApprox[i] = inverseNTT(zNTTMulA[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
H := sha3.NewSHAKE256()
|
H := sha3.NewSHAKE256()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user