| // Copyright 2019 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package note | |
| import ( | |
| "crypto/ed25519" | |
| "crypto/rand" | |
| "errors" | |
| "strings" | |
| "testing" | |
| "testing/iotest" | |
| ) | |
| func TestNewVerifier(t *testing.T) { | |
| vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW" | |
| _, err := NewVerifier(vkey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // Check various manglings are not accepted. | |
| badKey := func(k string) { | |
| _, err := NewVerifier(k) | |
| if err == nil { | |
| t.Errorf("NewVerifier(%q) succeeded, should have failed", k) | |
| } | |
| } | |
| b := []byte(vkey) | |
| for i := 0; i <= len(b); i++ { | |
| for j := i + 1; j <= len(b); j++ { | |
| if i != 0 || j != len(b) { | |
| badKey(string(b[i:j])) | |
| } | |
| } | |
| } | |
| for i := range b { | |
| b[i]++ | |
| badKey(string(b)) | |
| b[i]-- | |
| } | |
| badKey("PeterNeumann+cc469956+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TWBADKEY==") // wrong length key, with adjusted key hash | |
| badKey("PeterNeumann+173116ae+ZRpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW") // unknown algorithm, with adjusted key hash | |
| } | |
| func TestNewSigner(t *testing.T) { | |
| skey := "PRIVATE+KEY+PeterNeumann+c74f20a3+AYEKFALVFGyNhPJEMzD1QIDr+Y7hfZx09iUvxdXHKDFz" | |
| _, err := NewSigner(skey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // Check various manglings are not accepted. | |
| b := []byte(skey) | |
| for i := 0; i <= len(b); i++ { | |
| for j := i + 1; j <= len(b); j++ { | |
| if i == 0 && j == len(b) { | |
| continue | |
| } | |
| _, err := NewSigner(string(b[i:j])) | |
| if err == nil { | |
| t.Errorf("NewSigner(%q) succeeded, should have failed", b[i:j]) | |
| } | |
| } | |
| } | |
| for i := range b { | |
| b[i]++ | |
| _, err := NewSigner(string(b)) | |
| if err == nil { | |
| t.Errorf("NewSigner(%q) succeeded, should have failed", b) | |
| } | |
| b[i]-- | |
| } | |
| } | |
| 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) | |
| } | |
| if name := verifier.Name(); name != Name { | |
| t.Errorf("verifier.Name() = %q, want %q", name, Name) | |
| } | |
| shash := signer.KeyHash() | |
| vhash := verifier.KeyHash() | |
| if shash != vhash { | |
| t.Errorf("signer.KeyHash() = %#08x != verifier.KeyHash() = %#08x", shash, vhash) | |
| } | |
| msg := []byte("hi") | |
| sig, err := signer.Sign(msg) | |
| if err != nil { | |
| t.Fatalf("signer.Sign: %v", err) | |
| } | |
| if !verifier.Verify(msg, sig) { | |
| t.Fatalf("verifier.Verify failed on signature returned by signer.Sign") | |
| } | |
| sig[0]++ | |
| if verifier.Verify(msg, sig) { | |
| t.Fatalf("verifier.Verify succeeded on corrupt signature") | |
| } | |
| sig[0]-- | |
| msg[0]++ | |
| if verifier.Verify(msg, sig) { | |
| t.Fatalf("verifier.Verify succeeded 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 an error from rand reader. | |
| _, _, err = GenerateKey(iotest.TimeoutReader(iotest.OneByteReader(rand.Reader)), Name) | |
| if err == nil { | |
| t.Fatalf("GenerateKey succeeded with error-returning rand reader") | |
| } | |
| } | |
| 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) | |
| } | |
| vkey, err := NewEd25519VerifierKey(Name, pub) | |
| if err != nil { | |
| t.Fatalf("NewEd25519VerifierKey: %v", err) | |
| } | |
| verifier, err := NewVerifier(vkey) | |
| if err != nil { | |
| t.Fatalf("NewVerifier: %v", err) | |
| } | |
| testSignerAndVerifier(t, Name, signer, verifier) | |
| // Check that wrong key sizes return errors. | |
| _, err = NewEd25519VerifierKey(Name, pub[:len(pub)-1]) | |
| if err == nil { | |
| t.Errorf("NewEd25519VerifierKey succeeded with a seed of the wrong size") | |
| } | |
| } | |
| // newSignerFromEd25519Seed constructs a new signer from a verifier name and a | |
| // 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: 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" + | |
| "then you don't know what your problem is.\n" | |
| signer, err := NewSigner(skey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| msg, err := Sign(&Note{Text: text}, signer) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| want := `If you think cryptography is the answer to your problem, | |
| then you don't know what your problem is. | |
| — PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM= | |
| ` | |
| if string(msg) != want { | |
| t.Errorf("Sign: wrong output\nhave:\n%s\nwant:\n%s", msg, want) | |
| } | |
| // Check that existing signature is replaced by new one. | |
| msg, err = Sign(&Note{Text: text, Sigs: []Signature{{Name: "PeterNeumann", Hash: 0xc74f20a3, Base64: "BADSIGN="}}}, signer) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if string(msg) != want { | |
| t.Errorf("Sign replacing signature: wrong output\nhave:\n%s\nwant:\n%s", msg, want) | |
| } | |
| // Check various bad inputs. | |
| _, err = Sign(&Note{Text: "abc"}, signer) | |
| if err == nil || err.Error() != "malformed note" { | |
| t.Fatalf("Sign with short text: %v, want malformed note error", err) | |
| } | |
| _, err = Sign(&Note{Text: text, Sigs: []Signature{{Name: "a+b", Base64: "ABCD"}}}) | |
| if err == nil || err.Error() != "malformed note" { | |
| t.Fatalf("Sign with bad name: %v, want malformed note error", err) | |
| } | |
| _, err = Sign(&Note{Text: text, Sigs: []Signature{{Name: "PeterNeumann", Hash: 0xc74f20a3, Base64: "BADHASH="}}}) | |
| if err == nil || err.Error() != "malformed note" { | |
| t.Fatalf("Sign with bad pre-filled signature: %v, want malformed note error", err) | |
| } | |
| _, err = Sign(&Note{Text: text}, &badSigner{signer}) | |
| if err == nil || err.Error() != "invalid signer" { | |
| t.Fatalf("Sign with bad signer: %v, want invalid signer error", err) | |
| } | |
| _, err = Sign(&Note{Text: text}, &errSigner{signer}) | |
| if err != errSurprise { | |
| t.Fatalf("Sign with failing signer: %v, want errSurprise", err) | |
| } | |
| } | |
| func TestVerifierList(t *testing.T) { | |
| peterKey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW" | |
| peterVerifier, err := NewVerifier(peterKey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| enochKey := "EnochRoot+af0cfe78+ATtqJ7zOtqQtYqOo0CpvDXNlMhV3HeJDpjrASKGLWdop" | |
| enochVerifier, err := NewVerifier(enochKey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| list := VerifierList(peterVerifier, enochVerifier, enochVerifier) | |
| v, err := list.Verifier("PeterNeumann", 0xc74f20a3) | |
| if v != peterVerifier || err != nil { | |
| t.Fatalf("list.Verifier(peter) = %v, %v, want %v, nil", v, err, peterVerifier) | |
| } | |
| v, err = list.Verifier("PeterNeumann", 0xc74f20a4) | |
| if v != nil || err == nil || err.Error() != "unknown key PeterNeumann+c74f20a4" { | |
| t.Fatalf("list.Verifier(peter bad hash) = %v, %v, want nil, unknown key error", v, err) | |
| } | |
| v, err = list.Verifier("PeterNeuman", 0xc74f20a3) | |
| if v != nil || err == nil || err.Error() != "unknown key PeterNeuman+c74f20a3" { | |
| t.Fatalf("list.Verifier(peter bad name) = %v, %v, want nil, unknown key error", v, err) | |
| } | |
| v, err = list.Verifier("EnochRoot", 0xaf0cfe78) | |
| if v != nil || err == nil || err.Error() != "ambiguous key EnochRoot+af0cfe78" { | |
| t.Fatalf("list.Verifier(enoch) = %v, %v, want nil, ambiguous key error", v, err) | |
| } | |
| } | |
| type badSigner struct { | |
| Signer | |
| } | |
| func (b *badSigner) Name() string { | |
| return "bad name" | |
| } | |
| var errSurprise = errors.New("surprise!") | |
| type errSigner struct { | |
| Signer | |
| } | |
| func (e *errSigner) Sign([]byte) ([]byte, error) { | |
| return nil, errSurprise | |
| } | |
| type fixedVerifier struct{ v Verifier } | |
| func (v fixedVerifier) Verifier(name string, hash uint32) (Verifier, error) { | |
| return v.v, nil | |
| } | |
| func TestOpen(t *testing.T) { | |
| peterKey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW" | |
| peterVerifier, err := NewVerifier(peterKey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| enochKey := "EnochRoot+af0cfe78+ATtqJ7zOtqQtYqOo0CpvDXNlMhV3HeJDpjrASKGLWdop" | |
| enochVerifier, err := NewVerifier(enochKey) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| text := `If you think cryptography is the answer to your problem, | |
| then you don't know what your problem is. | |
| ` | |
| peterSig := "— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n" | |
| enochSig := "— EnochRoot rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ=\n" | |
| peter := Signature{"PeterNeumann", 0xc74f20a3, "x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM="} | |
| enoch := Signature{"EnochRoot", 0xaf0cfe78, "rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ="} | |
| // Check one signature verified, one not. | |
| n, err := Open([]byte(text+"\n"+peterSig+enochSig), VerifierList(peterVerifier)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if n.Text != text { | |
| t.Errorf("n.Text = %q, want %q", n.Text, text) | |
| } | |
| if len(n.Sigs) != 1 || n.Sigs[0] != peter { | |
| t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter}) | |
| } | |
| if len(n.UnverifiedSigs) != 1 || n.UnverifiedSigs[0] != enoch { | |
| t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter}) | |
| } | |
| // Check both verified. | |
| n, err = Open([]byte(text+"\n"+peterSig+enochSig), VerifierList(peterVerifier, enochVerifier)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if len(n.Sigs) != 2 || n.Sigs[0] != peter || n.Sigs[1] != enoch { | |
| t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter, enoch}) | |
| } | |
| if len(n.UnverifiedSigs) != 0 { | |
| t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.Sigs, []Signature{}) | |
| } | |
| // Check both unverified. | |
| n, err = Open([]byte(text+"\n"+peterSig+enochSig), VerifierList()) | |
| if n != nil || err == nil { | |
| t.Fatalf("Open unverified = %v, %v, want nil, error", n, err) | |
| } | |
| e, ok := err.(*UnverifiedNoteError) | |
| if !ok { | |
| t.Fatalf("Open unverified: err is %T, want *UnverifiedNoteError", err) | |
| } | |
| if err.Error() != "note has no verifiable signatures" { | |
| t.Fatalf("Open unverified: err.Error() = %q, want %q", err.Error(), "note has no verifiable signatures") | |
| } | |
| n = e.Note | |
| if n == nil { | |
| t.Fatalf("Open unverified: missing note in UnverifiedNoteError") | |
| } | |
| if len(n.Sigs) != 0 { | |
| t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{}) | |
| } | |
| if len(n.UnverifiedSigs) != 2 || n.UnverifiedSigs[0] != peter || n.UnverifiedSigs[1] != enoch { | |
| t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter, enoch}) | |
| } | |
| // Check duplicated verifier. | |
| _, err = Open([]byte(text+"\n"+enochSig), VerifierList(enochVerifier, peterVerifier, enochVerifier)) | |
| if err == nil || err.Error() != "ambiguous key EnochRoot+af0cfe78" { | |
| t.Fatalf("Open with duplicated verifier: err=%v, want ambiguous key", err) | |
| } | |
| // Check unused duplicated verifier. | |
| _, err = Open([]byte(text+"\n"+peterSig), VerifierList(enochVerifier, peterVerifier, enochVerifier)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // Check too many signatures. | |
| n, err = Open([]byte(text+"\n"+strings.Repeat(peterSig, 101)), VerifierList(peterVerifier)) | |
| if n != nil || err == nil || err.Error() != "malformed note" { | |
| t.Fatalf("Open too many verified signatures = %v, %v, want nil, malformed note error", n, err) | |
| } | |
| n, err = Open([]byte(text+"\n"+strings.Repeat(peterSig, 101)), VerifierList()) | |
| if n != nil || err == nil || err.Error() != "malformed note" { | |
| t.Fatalf("Open too many verified signatures = %v, %v, want nil, malformed note error", n, err) | |
| } | |
| // Invalid signature. | |
| n, err = Open([]byte(text+"\n"+peterSig[:60]+"ABCD"+peterSig[60:]), VerifierList(peterVerifier)) | |
| if n != nil || err == nil || err.Error() != "invalid signature for key PeterNeumann+c74f20a3" { | |
| t.Fatalf("Open too many verified signatures = %v, %v, want nil, invalid signature error", n, err) | |
| } | |
| // Duplicated verified and unverified signatures. | |
| enochABCD := Signature{"EnochRoot", 0xaf0cfe78, "rwz+eBzmZa0SO3NbfRGzPCpDckykFXSdeX+MNtCOXm2/5n" + "ABCD" + "2tiOHp+vAF1aGrQ5ovTG01oOTGwnWLox33WWd1RvMc+QQ="} | |
| n, err = Open([]byte(text+"\n"+peterSig+peterSig+enochSig+enochSig+enochSig[:60]+"ABCD"+enochSig[60:]), VerifierList(peterVerifier)) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if len(n.Sigs) != 1 || n.Sigs[0] != peter { | |
| t.Errorf("n.Sigs:\nhave %v\nwant %v", n.Sigs, []Signature{peter}) | |
| } | |
| if len(n.UnverifiedSigs) != 2 || n.UnverifiedSigs[0] != enoch || n.UnverifiedSigs[1] != enochABCD { | |
| t.Errorf("n.UnverifiedSigs:\nhave %v\nwant %v", n.UnverifiedSigs, []Signature{enoch, enochABCD}) | |
| } | |
| // Invalid encoded message syntax. | |
| badMsgs := []string{ | |
| text, | |
| text + "\n", | |
| text + "\n" + peterSig[:len(peterSig)-1], | |
| "\x01" + text + "\n" + peterSig, | |
| "\xff" + text + "\n" + peterSig, | |
| text + "\n" + "— Bad Name x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=", | |
| text + "\n" + peterSig + "Unexpected line.\n", | |
| } | |
| for _, msg := range badMsgs { | |
| n, err := Open([]byte(msg), VerifierList(peterVerifier)) | |
| if n != nil || err == nil || err.Error() != "malformed note" { | |
| t.Fatalf("Open bad msg = %v, %v, want nil, malformed note error\nmsg:\n%s", n, err, msg) | |
| } | |
| } | |
| // Verifiers returns a Verifier for the wrong name or hash. | |
| misnamedSig := strings.Replace(peterSig, "PeterNeumann", "CarmenSandiego", -1) | |
| _, err = Open([]byte(text+"\n"+misnamedSig), fixedVerifier{peterVerifier}) | |
| if err != errMismatchedVerifier { | |
| t.Fatalf("Open with wrong Verifier, err=%v, want errMismatchedVerifier", err) | |
| } | |
| wrongHash := strings.Replace(peterSig, "x08g", "xxxx", -1) | |
| _, err = Open([]byte(text+"\n"+wrongHash), fixedVerifier{peterVerifier}) | |
| if err != errMismatchedVerifier { | |
| t.Fatalf("Open with wrong Verifier, err=%v, want errMismatchedVerifier", err) | |
| } | |
| } | |
| func BenchmarkOpen(b *testing.B) { | |
| vkey := "PeterNeumann+c74f20a3+ARpc2QcUPDhMQegwxbzhKqiBfsVkmqq/LDE4izWy10TW" | |
| msg := []byte("If you think cryptography is the answer to your problem,\n" + | |
| "then you don't know what your problem is.\n" + | |
| "\n" + | |
| "— PeterNeumann x08go/ZJkuBS9UG/SffcvIAQxVBtiFupLLr8pAcElZInNIuGUgYN1FFYC2pZSNXgKvqfqdngotpRZb6KE6RyyBwJnAM=\n") | |
| verifier, err := NewVerifier(vkey) | |
| if err != nil { | |
| b.Fatal(err) | |
| } | |
| verifiers := VerifierList(verifier) | |
| verifiers0 := VerifierList() | |
| // Try with 0 signatures and 1 signature so we can tell how much each signature adds. | |
| b.Run("Sig0", func(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| _, err := Open(msg, verifiers0) | |
| e, ok := err.(*UnverifiedNoteError) | |
| if !ok { | |
| b.Fatal("expected UnverifiedNoteError") | |
| } | |
| n := e.Note | |
| if len(n.Sigs) != 0 || len(n.UnverifiedSigs) != 1 { | |
| b.Fatal("wrong signature count") | |
| } | |
| } | |
| }) | |
| b.Run("Sig1", func(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| n, err := Open(msg, verifiers) | |
| if err != nil { | |
| b.Fatal(err) | |
| } | |
| if len(n.Sigs) != 1 || len(n.UnverifiedSigs) != 0 { | |
| b.Fatal("wrong signature count") | |
| } | |
| } | |
| }) | |
| } |