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" +