Skip to content

Commit 3a29f91

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

2 files changed

Lines changed: 121 additions & 17 deletions

File tree

note/note_cosigv1.go

Lines changed: 46 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,49 @@ 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+
h := keyHashMLDSA(name, pubKeyBytes)
100+
s := &subtreeSigner{
101+
name: name,
102+
hash: h,
103+
}
104+
82105
s.signNote = func(msg []byte) ([]byte, error) {
83106
t := uint64(time.Now().Unix())
84107
c := &log.Checkpoint{}
@@ -92,7 +115,7 @@ func newMLDSASigner(name string, keyBytes []byte) (*subtreeSigner, error) {
92115
if err != nil {
93116
return nil, err
94117
}
95-
sB, err := key.Sign(nil, m, nil)
118+
sB, err := signer.Sign(nil, m, nil)
96119
if err != nil {
97120
return nil, err
98121
}
@@ -192,13 +215,20 @@ func NewSignerForCosignatureV1(skey string) (Signer, error) {
192215
s.verify = verifyEd25519CosigV1(pubkey[1:])
193216

194217
case algMLDSA44:
195-
stSigner, err := newMLDSASigner(name, key)
218+
if len(key) != mldsa.PrivateKeySize {
219+
return nil, errSignerID
220+
}
221+
priv, err := mldsa.NewPrivateKey(mldsa.MLDSA44(), key)
222+
if err != nil {
223+
return nil, err
224+
}
225+
stSigner, err := NewMLDSASignerFromCrypto(name, priv)
196226
if err != nil {
197227
return nil, err
198228
}
199229
s.sign = stSigner.Sign
200-
s.verify = stSigner.verifier.verifyNote
201-
s.hash = stSigner.hash
230+
s.verify = stSigner.Verifier().Verify
231+
s.hash = stSigner.KeyHash()
202232
}
203233

204234
return s, nil
@@ -373,7 +403,7 @@ func formatEd25519CosignatureV1(t uint64, msg []byte) ([]byte, error) {
373403
if lines := bytes.Split(msg, []byte("\n")); len(lines) < 3 {
374404
return nil, errors.New("cosigned note format invalid")
375405
}
376-
return []byte(fmt.Sprintf("cosignature/v1\ntime %d\n%s", t, msg)), nil
406+
return fmt.Appendf(nil, "cosignature/v1\ntime %d\n%s", t, msg), nil
377407
}
378408

379409
func formatMLDSACosignatureV1(cosignerName string, timestamp uint64, logOrigin string, start, end uint64, hash []byte) ([]byte, error) {
@@ -417,7 +447,6 @@ var (
417447
errInvalidTimestamp = errors.New("invalid timestamp")
418448
)
419449

420-
421450
// SubtreeSigner is a note.Signer that can additionally produce subtree signatures, and
422451
// provide access to a similarly capable verifier.
423452
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)