notary/internal/note: add NewVerifierFromEd25519PublicKey
External implementations of Signer and Verifier, for example those
backed by an HSM, might not want to reimplement the key hash algorithm.
Provide NewVerifierFromEd25519PublicKey for them to obtain a Verifier
(along with its KeyHash method) from a plain Ed25519 public key.
Change-Id: I6a5a40c6cf66152a90ccb012d3eeff73aa2e851a
Reviewed-on: https://go-review.googlesource.com/c/exp/+/168779
Reviewed-by: Katie Hockman <katie@golang.org>
diff --git a/notary/internal/note/note.go b/notary/internal/note/note.go
index d4b14cf..3dcbc46 100644
--- a/notary/internal/note/note.go
+++ b/notary/internal/note/note.go
@@ -381,6 +381,26 @@
return skey, vkey, nil
}
+// NewVerifierFromEd25519PublicKey constructs a new verifier from a notary name
+// and a golang.org/x/crypto/ed25519 public key.
+func NewVerifierFromEd25519PublicKey(name string, pub ed25519.PublicKey) (Verifier, error) {
+ if len(pub) != ed25519.PublicKeySize {
+ return nil, fmt.Errorf("invalid public key size %d, expected %d", len(pub), ed25519.PublicKeySize)
+ }
+
+ pubkey := append([]byte{algEd25519}, pub...)
+ hash := keyHash(name, pubkey)
+
+ v := &verifier{
+ name: name,
+ hash: uint32(hash),
+ verify: func(msg, sig []byte) bool {
+ return ed25519.Verify(pub, msg, sig)
+ },
+ }
+ return v, nil
+}
+
// A Notaries is a collection of known notary keys.
type Notaries interface {
// Verifier returns the Verifier associated with the notary key
diff --git a/notary/internal/note/note_test.go b/notary/internal/note/note_test.go
index 3c46ca6..11e722e 100644
--- a/notary/internal/note/note_test.go
+++ b/notary/internal/note/note_test.go
@@ -11,6 +11,8 @@
"strings"
"testing"
"testing/iotest"
+
+ "golang.org/x/crypto/ed25519"
)
func TestNewVerifier(t *testing.T) {
@@ -76,22 +78,7 @@
}
}
-func TestGenerateKey(t *testing.T) {
- // Generate key pair, make sure it is all self-consistent.
- const Name = "EnochRoot"
- skey, vkey, err := GenerateKey(rand.Reader, Name)
- if err != nil {
- t.Fatalf("GenerateKey: %v", err)
- }
- signer, err := NewSigner(skey)
- if err != nil {
- t.Fatalf("NewSigner: %v", err)
- }
- verifier, err := NewVerifier(vkey)
- if err != nil {
- t.Fatalf("NewVerifier: %v", err)
- }
-
+func testSignerAndVerifier(t *testing.T, Name string, signer Signer, verifier Verifier) {
if name := signer.Name(); name != Name {
t.Errorf("signer.Name() = %q, want %q", name, Name)
}
@@ -116,6 +103,31 @@
if verifier.Verify(msg, sig) {
t.Fatalf("verifier.Verify succceeded on corrupt signature")
}
+ sig[0]--
+ msg[0]++
+ if verifier.Verify(msg, sig) {
+ t.Fatalf("verifier.Verify succceeded on corrupt message")
+ }
+}
+
+func TestGenerateKey(t *testing.T) {
+ // Generate key pair, make sure it is all self-consistent.
+ const Name = "EnochRoot"
+
+ skey, vkey, err := GenerateKey(rand.Reader, Name)
+ if err != nil {
+ t.Fatalf("GenerateKey: %v", err)
+ }
+ signer, err := NewSigner(skey)
+ if err != nil {
+ t.Fatalf("NewSigner: %v", err)
+ }
+ verifier, err := NewVerifier(vkey)
+ if err != nil {
+ t.Fatalf("NewVerifier: %v", err)
+ }
+
+ testSignerAndVerifier(t, Name, signer, verifier)
// Check that GenerateKey returns error from rand reader.
_, _, err = GenerateKey(iotest.TimeoutReader(iotest.OneByteReader(rand.Reader)), Name)
@@ -124,6 +136,57 @@
}
}
+func TestFromEd25519(t *testing.T) {
+ const Name = "EnochRoot"
+
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatalf("GenerateKey: %v", err)
+ }
+ signer, err := newSignerFromEd25519Seed(Name, priv.Seed())
+ if err != nil {
+ t.Fatalf("newSignerFromEd25519Seed: %v", err)
+ }
+ verifier, err := NewVerifierFromEd25519PublicKey(Name, pub)
+ if err != nil {
+ t.Fatalf("NewVerifierFromEd25519PublicKey: %v", err)
+ }
+
+ testSignerAndVerifier(t, Name, signer, verifier)
+
+ // Check that wrong key sizes return errors.
+ _, err = newSignerFromEd25519Seed(Name, priv)
+ if err == nil {
+ t.Errorf("newSignerFromEd25519Seed succeeded with a seed of the wrong size")
+ }
+ _, err = NewVerifierFromEd25519PublicKey(Name, pub[:len(pub)-1])
+ if err == nil {
+ t.Errorf("NewVerifierFromEd25519PublicKey succeeded with a seed of the wrong size")
+ }
+}
+
+// newSignerFromEd25519Seed constructs a new signer from a notary name and a
+// golang.org/x/crypto/ed25519 private key seed.
+func newSignerFromEd25519Seed(name string, seed []byte) (Signer, error) {
+ if len(seed) != ed25519.SeedSize {
+ return nil, errors.New("invalid seed size")
+ }
+ priv := ed25519.NewKeyFromSeed(seed)
+ pub := priv[32:]
+
+ pubkey := append([]byte{algEd25519}, pub...)
+ hash := keyHash(name, pubkey)
+
+ s := &signer{
+ name: name,
+ hash: uint32(hash),
+ sign: func(msg []byte) ([]byte, error) {
+ return ed25519.Sign(priv, msg), nil
+ },
+ }
+ return s, nil
+}
+
func TestSign(t *testing.T) {
skey := "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz"
text := "If you think cryptography is the answer to your problem,\n" +