Skip to content

Commit 2435170

Browse files
authored
mldsa,slhdsa: crypto.Signer assertion
1 parent fd2eedf commit 2435170

10 files changed

Lines changed: 111 additions & 44 deletions

File tree

mldsa/field.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,19 @@ func vectorCountOnes(a []ringElement) int {
243243
}
244244
return oneCount
245245
}
246+
247+
func constantTimeEqualRingElement(a, b ringElement) int {
248+
var res int32
249+
for i := range a {
250+
res |= int32(a[i] ^ b[i])
251+
}
252+
return subtle.ConstantTimeByteEq(byte(res|(-res)>>31), 0)
253+
}
254+
255+
func constantTimeEqualRingElementArray(a, b []ringElement) int {
256+
eq := 1
257+
for i := range a {
258+
eq &= constantTimeEqualRingElement(a[i], b[i])
259+
}
260+
return eq
261+
}

mldsa/mldsa44.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ const (
101101
sigEncodedLen87 = lambda256/4 + encodingSize20*l87 + omega75 + k87
102102
)
103103

104+
var _ crypto.Signer = (*PrivateKey44)(nil)
105+
var _ crypto.Signer = (*Key44)(nil)
106+
104107
// A PrivateKey44 is the private key for the ML-DSA-44 signature scheme.
105108
type PrivateKey44 struct {
106109
rho [32]byte // public random seed
@@ -118,10 +121,10 @@ type PrivateKey44 struct {
118121
t1Once sync.Once
119122
}
120123

121-
// PublicKey returns the public key corresponding to the private key.
124+
// Public returns the public key corresponding to the private key.
122125
// Although we can derive the public key from the private key,
123126
// but we do NOT need to derive it at most of the time.
124-
func (sk *PrivateKey44) PublicKey() crypto.PublicKey {
127+
func (sk *PrivateKey44) Public() crypto.PublicKey {
125128
sk.ensureT1()
126129
return &PublicKey44{
127130
rho: sk.rho,
@@ -187,9 +190,9 @@ type PublicKey44 struct {
187190
nttOnce sync.Once
188191
}
189192

190-
// PublicKey generates and returns the corresponding public key for the given
193+
// Public generates and returns the corresponding public key for the given
191194
// Key44 instance.
192-
func (sk *Key44) PublicKey() *PublicKey44 {
195+
func (sk *Key44) Public() crypto.PublicKey {
193196
return &PublicKey44{
194197
rho: sk.rho,
195198
t1: sk.t1,
@@ -210,9 +213,9 @@ func (pk *PublicKey44) Equal(x crypto.PublicKey) bool {
210213
if !ok {
211214
return false
212215
}
213-
b1 := pk.Bytes()
214-
b2 := xx.Bytes()
215-
return subtle.ConstantTimeCompare(b1, b2) == 1
216+
eq := subtle.ConstantTimeCompare(pk.rho[:], xx.rho[:]) &
217+
constantTimeEqualRingElementArray(pk.t1[:], xx.t1[:])
218+
return eq == 1
216219
}
217220

218221
// Bytes converts the PublicKey44 instance into a byte slice.
@@ -271,9 +274,13 @@ func (sk *PrivateKey44) Equal(x any) bool {
271274
if !ok {
272275
return false
273276
}
274-
b1 := sk.Bytes()
275-
b2 := xx.Bytes()
276-
return subtle.ConstantTimeCompare(b1, b2) == 1
277+
eq := subtle.ConstantTimeCompare(sk.rho[:], xx.rho[:]) &
278+
subtle.ConstantTimeCompare(sk.k[:], xx.k[:]) &
279+
subtle.ConstantTimeCompare(sk.tr[:], xx.tr[:]) &
280+
constantTimeEqualRingElementArray(sk.s1[:], xx.s1[:]) &
281+
constantTimeEqualRingElementArray(sk.s2[:], xx.s2[:]) &
282+
constantTimeEqualRingElementArray(sk.t0[:], xx.t0[:])
283+
return eq == 1
277284
}
278285

279286
// GenerateKey44 generates a new Key44 (ML-DSA-44) using the provided random source.
@@ -363,7 +370,7 @@ func dsaKeyGen44(sk *Key44, xi *[32]byte) {
363370
}
364371
}
365372
H.Reset()
366-
ek := sk.PublicKey().Bytes()
373+
ek := sk.Public().(*PublicKey44).Bytes()
367374
H.Write(ek)
368375
H.Read(sk.tr[:])
369376
}

mldsa/mldsa44_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestKeyGen44(t *testing.T) {
4646
if err != nil {
4747
t.Fatalf("NewPrivateKey44 failed: %v", err)
4848
}
49-
pub := priv.PublicKey()
49+
pub := priv.Public().(*PublicKey44)
5050
pubBytes := pub.Bytes()
5151
if !bytes.Equal(pubBytes, pk) {
5252
t.Errorf("Public key mismatch: got %x, want %x", pubBytes, pk)
@@ -70,7 +70,7 @@ func TestKeyGen44(t *testing.T) {
7070
if !priv.Equal(priv2) {
7171
t.Errorf("Private key not equal: got %x, want %x", privBytes, priv2.Bytes())
7272
}
73-
pub3 := priv2.PublicKey()
73+
pub3 := priv2.Public()
7474
if !pub.Equal(pub3) {
7575
t.Errorf("Public key from private key not equal")
7676
}
@@ -127,6 +127,7 @@ func TestSign44(t *testing.T) {
127127
if err != nil {
128128
t.Fatalf("NewPrivateKey44 failed: %v", err)
129129
}
130+
130131
sig2, err := priv.signInternal(seed[:], mu)
131132
if err != nil {
132133
t.Fatalf("failed to sign: %v", err)

mldsa/mldsa65.go

Lines changed: 18 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mldsa/mldsa65_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestKeyGen65(t *testing.T) {
4646
if err != nil {
4747
t.Fatalf("NewPrivateKey65 failed: %v", err)
4848
}
49-
pub := priv.PublicKey()
49+
pub := priv.Public().(*PublicKey65)
5050
pubBytes := pub.Bytes()
5151
if !bytes.Equal(pubBytes, pk) {
5252
t.Errorf("Public key mismatch: got %x, want %x", pubBytes, pk)
@@ -70,7 +70,7 @@ func TestKeyGen65(t *testing.T) {
7070
if !priv.Equal(priv2) {
7171
t.Errorf("Private key not equal: got %x, want %x", privBytes, priv2.Bytes())
7272
}
73-
pub3 := priv2.PublicKey()
73+
pub3 := priv2.Public()
7474
if !pub.Equal(pub3) {
7575
t.Errorf("Public key from private key not equal")
7676
}

mldsa/mldsa87.go

Lines changed: 18 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mldsa/mldsa87_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestKeyGen87(t *testing.T) {
4646
if err != nil {
4747
t.Fatalf("NewPrivateKey65 failed: %v", err)
4848
}
49-
pub := priv.PublicKey()
49+
pub := priv.Public().(*PublicKey87)
5050
pubBytes := pub.Bytes()
5151
if !bytes.Equal(pubBytes, pk) {
5252
t.Errorf("Public key mismatch: got %x, want %x", pubBytes, pk)
@@ -70,7 +70,7 @@ func TestKeyGen87(t *testing.T) {
7070
if !priv.Equal(priv2) {
7171
t.Errorf("Private key not equal: got %x, want %x", privBytes, priv2.Bytes())
7272
}
73-
pub3 := priv2.PublicKey()
73+
pub3 := priv2.Public()
7474
if !pub.Equal(pub3) {
7575
t.Errorf("Public key from private key not equal")
7676
}

slhdsa/dsa.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,42 @@
77
package slhdsa
88

99
import (
10+
"crypto"
1011
"errors"
12+
"io"
1113
)
1214

15+
var _ crypto.Signer = (*PrivateKey)(nil)
16+
17+
type Options struct {
18+
Context []byte
19+
AddRand []byte // optional randomness to be added to the signature. If nil, the signature is deterministic.
20+
}
21+
22+
func (opts *Options) HashFunc() crypto.Hash {
23+
return crypto.Hash(0)
24+
}
25+
26+
// Sign produces a signature of the message using the private key.
27+
// It is a wrapper around the SignMessage method, implementing the crypto.Signer interface.
28+
func (sk *PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
29+
return sk.SignMessage(rand, message, opts)
30+
}
31+
1332
// Sign generates a pure SLH-DSA signature for the given message.
1433
// The signature is deterministic if the addRand parameter is nil.
1534
// If addRand is not nil, it must be of the same length as n.
1635
//
1736
// See FIPS 205 Algorithm 22 slh_sign
18-
func (sk *PrivateKey) Sign(message, context, addRand []byte) ([]byte, error) {
37+
func (sk *PrivateKey) SignMessage(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
1938
if len(message) == 0 {
2039
return nil, errors.New("slhdsa: empty message")
2140
}
41+
var context, addRand []byte
42+
if opts, ok := opts.(*Options); ok {
43+
context = opts.Context
44+
addRand = opts.AddRand
45+
}
2246
if len(addRand) > 0 && len(addRand) != int(sk.params.n) {
2347
return nil, errors.New("slhdsa: addrnd should be nil (deterministic variant) or of length n")
2448
}
@@ -85,10 +109,14 @@ func (sk *PrivateKey) signInternal(msgPrefix, message, addRand []byte) ([]byte,
85109
// Verify verifies a pure SLH-DSA signature for the given message.
86110
//
87111
// See FIPS 205 Algorithm 24 slh_verify
88-
func (pk *PublicKey) Verify(signature, message, context []byte) bool {
112+
func (pk *PublicKey) VerifyWithOptions(signature, message []byte, opts crypto.SignerOpts) bool {
89113
if len(message) == 0 {
90114
return false
91115
}
116+
var context []byte
117+
if opts, ok := opts.(*Options); ok {
118+
context = opts.Context
119+
}
92120
if len(context) > maxContextLen {
93121
return false
94122
}

slhdsa/dsa_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func testData(t *testing.T, filename string, tc *slhtest) {
7575
if err != nil {
7676
t.Fatalf("%v NewPrivateKey(%x) = %v", filename, skBytes, err)
7777
}
78-
sig2, err := privKey.Sign(message, context, addRand)
78+
sig2, err := privKey.Sign(nil, message, &Options{context, addRand})
7979
if err != nil {
8080
t.Fatalf("%v Sign(%x,%x) = %v", filename, message, context, err)
8181
}
@@ -104,7 +104,7 @@ func testData(t *testing.T, filename string, tc *slhtest) {
104104
if err != nil {
105105
t.Fatalf("%v NewPublicKey(%x) = %v", filename, pkBytes, err)
106106
}
107-
if !pub.Verify(sigOriginal, message, context) {
107+
if !pub.VerifyWithOptions(sigOriginal, message, &Options{Context: context}) {
108108
t.Errorf("%v Verify() = false, want true", filename)
109109
}
110110
}

slhdsa/key.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package slhdsa
88

99
import (
10+
"crypto"
1011
"crypto/sha256"
1112
"crypto/sha3"
1213
"crypto/sha512"
@@ -69,7 +70,7 @@ func (sk *PrivateKey) Bytes() []byte {
6970
}
7071

7172
// Public returns the public key of the private key.
72-
func (sk *PrivateKey) Public() *PublicKey {
73+
func (sk *PrivateKey) Public() crypto.PublicKey {
7374
return &sk.PublicKey
7475
}
7576

0 commit comments

Comments
 (0)