Skip to content

Commit e627221

Browse files
committed
Add support for crypto.Signer backed MLDSA
1 parent 38e6e69 commit e627221

2 files changed

Lines changed: 120 additions & 17 deletions

File tree

note/note_cosigv1.go

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package note
66

77
import (
88
"bytes"
9+
"crypto"
910
"crypto/ed25519"
1011
"crypto/sha256"
1112
"encoding/base64"
@@ -58,27 +59,48 @@ func NewMLDSASigner(skey string) (SubtreeSigner, error) {
5859
if priv1 != "PRIVATE" || priv2 != "KEY" || len(hash16) != 8 || err != nil || !isValidName(name) || len(key) == 0 {
5960
return nil, errSignerID
6061
}
62+
hash, err := strconv.ParseUint(hash16, 16, 32)
63+
if err != nil {
64+
return nil, errInvalidHash
65+
}
66+
6167
alg, key := key[0], key[1:]
6268
if alg != algMLDSA44 {
6369
return nil, errSignerID
6470
}
65-
return newMLDSASigner(name, key)
66-
}
67-
68-
// newMLDSASigner returns a signer for MLDSA cosignature v1, with the provided
69-
// name and key bytes in the format: algo || private key.
70-
func newMLDSASigner(name string, keyBytes []byte) (*subtreeSigner, error) {
71-
s := &subtreeSigner{name: name}
72-
if len(keyBytes) != mldsa.PrivateKeySize {
71+
if len(key) != mldsa.PrivateKeySize {
7372
return nil, errSignerID
7473
}
75-
key, err := mldsa.NewPrivateKey(mldsa.MLDSA44(), keyBytes)
74+
priv, err := mldsa.NewPrivateKey(mldsa.MLDSA44(), key)
75+
if err != nil {
76+
return nil, err
77+
}
78+
s, err := NewMLDSASignerFromCrypto(name, priv)
7679
if err != nil {
7780
return nil, err
7881
}
79-
pubKey := key.PublicKey()
82+
if s.KeyHash() != uint32(hash) {
83+
return nil, errInvalidHash
84+
}
85+
return s, nil
86+
}
87+
88+
// NewMLDSASignerFromCrypto returns a subtree signer for MLDSA cosignature v1 which uses an underlying crypto.Signer for cryptographic operations.
89+
func NewMLDSASignerFromCrypto(name string, signer crypto.Signer) (SubtreeSigner, error) {
90+
if !isValidName(name) {
91+
return nil, errSignerID
92+
}
93+
pubKey, ok := signer.Public().(*mldsa.PublicKey)
94+
if !ok {
95+
return nil, errSignerAlg
96+
}
8097
pubKeyBytes := append([]byte{algMLDSA44}, pubKey.Bytes()...)
81-
s.hash = keyHashMLDSA(name, pubKeyBytes)
98+
99+
s := &subtreeSigner{
100+
name: name,
101+
hash: keyHashMLDSA(name, pubKeyBytes),
102+
}
103+
82104
s.signNote = func(msg []byte) ([]byte, error) {
83105
t := uint64(time.Now().Unix())
84106
c := &log.Checkpoint{}
@@ -92,7 +114,7 @@ func newMLDSASigner(name string, keyBytes []byte) (*subtreeSigner, error) {
92114
if err != nil {
93115
return nil, err
94116
}
95-
sB, err := key.Sign(nil, m, nil)
117+
sB, err := signer.Sign(nil, m, nil)
96118
if err != nil {
97119
return nil, err
98120
}
@@ -192,13 +214,20 @@ func NewSignerForCosignatureV1(skey string) (Signer, error) {
192214
s.verify = verifyEd25519CosigV1(pubkey[1:])
193215

194216
case algMLDSA44:
195-
stSigner, err := newMLDSASigner(name, key)
217+
if len(key) != mldsa.PrivateKeySize {
218+
return nil, errSignerID
219+
}
220+
priv, err := mldsa.NewPrivateKey(mldsa.MLDSA44(), key)
221+
if err != nil {
222+
return nil, err
223+
}
224+
stSigner, err := NewMLDSASignerFromCrypto(name, priv)
196225
if err != nil {
197226
return nil, err
198227
}
199228
s.sign = stSigner.Sign
200-
s.verify = stSigner.verifier.verifyNote
201-
s.hash = stSigner.hash
229+
s.verify = stSigner.Verifier().Verify
230+
s.hash = stSigner.KeyHash()
202231
}
203232

204233
return s, nil
@@ -373,7 +402,7 @@ func formatEd25519CosignatureV1(t uint64, msg []byte) ([]byte, error) {
373402
if lines := bytes.Split(msg, []byte("\n")); len(lines) < 3 {
374403
return nil, errors.New("cosigned note format invalid")
375404
}
376-
return []byte(fmt.Sprintf("cosignature/v1\ntime %d\n%s", t, msg)), nil
405+
return fmt.Appendf(nil, "cosignature/v1\ntime %d\n%s", t, msg), nil
377406
}
378407

379408
func formatMLDSACosignatureV1(cosignerName string, timestamp uint64, logOrigin string, start, end uint64, hash []byte) ([]byte, error) {
@@ -417,7 +446,6 @@ var (
417446
errInvalidTimestamp = errors.New("invalid timestamp")
418447
)
419448

420-
421449
// SubtreeSigner is a note.Signer that can additionally produce subtree signatures, and
422450
// provide access to a similarly capable verifier.
423451
type SubtreeSigner interface {

note/note_cosigv1_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
package note
66

77
import (
8+
"crypto"
9+
"crypto/ecdsa"
10+
"crypto/elliptic"
811
"crypto/rand"
912
"strings"
1013
"testing"
1114
"time"
1215

16+
"filippo.io/mldsa"
1317
"golang.org/x/mod/sumdb/note"
1418
)
1519

@@ -427,3 +431,74 @@ func mustGenerateMLDSAKey(t *testing.T, name string) (string, string) {
427431
}
428432
return skey, vkey
429433
}
434+
435+
func TestMLDSASignerFromCrypto(t *testing.T) {
436+
const name = "mldsa-test"
437+
438+
for _, test := range []struct {
439+
name string
440+
signer crypto.Signer
441+
wantErr bool
442+
}{
443+
{
444+
name: "valid MLDSA signer",
445+
signer: mustMLDSASigner(t),
446+
},
447+
{
448+
name: "invalid signer (not MLDSA)",
449+
signer: mustECDSASigner(t),
450+
wantErr: true,
451+
},
452+
} {
453+
t.Run(test.name, func(t *testing.T) {
454+
signer, err := NewMLDSASignerFromCrypto(name, test.signer)
455+
if gotErr := err != nil; gotErr != test.wantErr {
456+
t.Fatalf("NewMLDSASignerFromCrypto: got err %v, wantErr %v", err, test.wantErr)
457+
}
458+
if test.wantErr {
459+
return
460+
}
461+
462+
if signer.Name() != name {
463+
t.Errorf("signer.Name() = %q, want %q", signer.Name(), name)
464+
}
465+
466+
origin := "test-log"
467+
var start uint64 = 0
468+
var end uint64 = 10
469+
root := make([]byte, 32)
470+
if _, err := rand.Read(root); err != nil {
471+
t.Fatal(err)
472+
}
473+
timestamp := uint64(time.Now().Unix())
474+
475+
sig, err := signer.SignSubtree(timestamp, origin, start, end, root)
476+
if err != nil {
477+
t.Fatalf("SignSubtree: %v", err)
478+
}
479+
480+
verifier := signer.Verifier()
481+
if !verifier.VerifySubtree(timestamp, origin, start, end, root, sig) {
482+
t.Error("VerifySubtree failed")
483+
}
484+
})
485+
}
486+
}
487+
488+
func mustMLDSASigner(t *testing.T) crypto.Signer {
489+
t.Helper()
490+
mldsaK, err := mldsa.GenerateKey(mldsa.MLDSA44())
491+
if err != nil {
492+
t.Fatal(err)
493+
}
494+
return mldsaK
495+
}
496+
497+
func mustECDSASigner(t *testing.T) crypto.Signer {
498+
t.Helper()
499+
ecdsaK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
500+
if err != nil {
501+
t.Fatal(err)
502+
}
503+
return ecdsaK
504+
}

0 commit comments

Comments
 (0)