docs/tuf/signed/sign_test.go

307 lines
8.5 KiB
Go

package signed
import (
"crypto/rand"
"encoding/pem"
"io"
"testing"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/stretchr/testify/require"
)
const (
testKeyPEM1 = "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnKuXZeefa2LmgxaL5NsM\nzKOHNe+x/nL6ik+lDBCTV6OdcwAhHQS+PONGhrChIUVR6Vth3hUCrreLzPO73Oo5\nVSCuRJ53UronENl6lsa5mFKP8StYLvIDITNvkoT3j52BJIjyNUK9UKY9As2TNqDf\nBEPIRp28ev/NViwGOEkBu2UAbwCIdnDXm8JQErCZA0Ydm7PKGgjLbFsFGrVzqXHK\n6pdzJXlhr9yap3UpgQ/iO9JtoEYB2EXsnSrPc9JRjR30bNHHtnVql3fvinXrAEwq\n3xmN4p+R4VGzfdQN+8Kl/IPjqWB535twhFYEG/B7Ze8IwbygBjK3co/KnOPqMUrM\nBI8ztvPiogz+MvXb8WvarZ6TMTh8ifZI96r7zzqyzjR1hJulEy3IsMGvz8XS2J0X\n7sXoaqszEtXdq5ef5zKVxkiyIQZcbPgmpHLq4MgfdryuVVc/RPASoRIXG4lKaTJj\n1ANMFPxDQpHudCLxwCzjCb+sVa20HBRPTnzo8LSZkI6jAgMBAAE=\n-----END PUBLIC KEY-----"
testKeyID1 = "51324b59d4888faa91219ebbe5a3876bb4efb21f0602ddf363cd4c3996ded3d4"
)
type FailingCryptoService struct {
testKey data.PrivateKey
}
func (mts *FailingCryptoService) Create(_, _ string) (data.PublicKey, error) {
return mts.testKey, nil
}
func (mts *FailingCryptoService) ListKeys(role string) []string {
return []string{mts.testKey.ID()}
}
func (mts *FailingCryptoService) ListAllKeys() map[string]string {
return map[string]string{
mts.testKey.ID(): data.CanonicalRootRole,
mts.testKey.ID(): data.CanonicalTargetsRole,
mts.testKey.ID(): data.CanonicalSnapshotRole,
mts.testKey.ID(): data.CanonicalTimestampRole,
}
}
func (mts *FailingCryptoService) GetKey(keyID string) data.PublicKey {
if keyID == "testID" {
return data.PublicKeyFromPrivate(mts.testKey)
}
return nil
}
func (mts *FailingCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
if mts.testKey != nil {
return mts.testKey, "testRole", nil
}
return nil, "", trustmanager.ErrKeyNotFound{KeyID: keyID}
}
func (mts *FailingCryptoService) RemoveKey(keyID string) error {
return nil
}
func (mts *FailingCryptoService) ImportRootKey(r io.Reader) error {
return nil
}
type MockCryptoService struct {
testKey data.PrivateKey
}
func (mts *MockCryptoService) Create(_ string, _ string) (data.PublicKey, error) {
return mts.testKey, nil
}
func (mts *MockCryptoService) GetKey(keyID string) data.PublicKey {
if keyID == "testID" {
return data.PublicKeyFromPrivate(mts.testKey)
}
return nil
}
func (mts *MockCryptoService) ListKeys(role string) []string {
return []string{mts.testKey.ID()}
}
func (mts *MockCryptoService) ListAllKeys() map[string]string {
return map[string]string{
mts.testKey.ID(): data.CanonicalRootRole,
mts.testKey.ID(): data.CanonicalTargetsRole,
mts.testKey.ID(): data.CanonicalSnapshotRole,
mts.testKey.ID(): data.CanonicalTimestampRole,
}
}
func (mts *MockCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
return mts.testKey, "testRole", nil
}
func (mts *MockCryptoService) RemoveKey(keyID string) error {
return nil
}
func (mts *MockCryptoService) ImportRootKey(r io.Reader) error {
return nil
}
var _ CryptoService = &MockCryptoService{}
type StrictMockCryptoService struct {
MockCryptoService
}
func (mts *StrictMockCryptoService) GetKey(keyID string) data.PublicKey {
if keyID == mts.testKey.ID() {
return data.PublicKeyFromPrivate(mts.testKey)
}
return nil
}
func (mts *StrictMockCryptoService) ListKeys(role string) []string {
return []string{mts.testKey.ID()}
}
func (mts *StrictMockCryptoService) ListAllKeys() map[string]string {
return map[string]string{
mts.testKey.ID(): data.CanonicalRootRole,
mts.testKey.ID(): data.CanonicalTargetsRole,
mts.testKey.ID(): data.CanonicalSnapshotRole,
mts.testKey.ID(): data.CanonicalTimestampRole,
}
}
func (mts *StrictMockCryptoService) ImportRootKey(r io.Reader) error {
return nil
}
// Test signing and ensure the expected signature is added
func TestBasicSign(t *testing.T) {
cs := NewEd25519()
key, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
testData := data.Signed{}
err = Sign(cs, &testData, key)
require.NoError(t, err)
if len(testData.Signatures) != 1 {
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
}
if testData.Signatures[0].KeyID != key.ID() {
t.Fatalf("Wrong signature ID returned: %s", testData.Signatures[0].KeyID)
}
}
// Signing with the same key multiple times should not produce multiple sigs
// with the same key ID
func TestReSign(t *testing.T) {
cs := NewEd25519()
key, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
testData := data.Signed{}
Sign(cs, &testData, key)
Sign(cs, &testData, key)
if len(testData.Signatures) != 1 {
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
}
if testData.Signatures[0].KeyID != key.ID() {
t.Fatalf("Wrong signature ID returned: %s", testData.Signatures[0].KeyID)
}
}
// Should not remove signatures for valid keys that were not resigned with
func TestMultiSign(t *testing.T) {
cs := NewEd25519()
testData := data.Signed{}
key1, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
Sign(cs, &testData, key1)
// reinitializing cs means it won't know about key1. We want
// to attempt to sign passing both key1 and key2, while expecting
// that the signature for key1 is left intact and the signature
// for key2 is added
cs = NewEd25519()
key2, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
Sign(
cs,
&testData,
key1,
key2,
)
if len(testData.Signatures) != 2 {
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
}
keyIDs := map[string]struct{}{key1.ID(): {}, key2.ID(): {}}
count := 0
for _, sig := range testData.Signatures {
count++
if _, ok := keyIDs[sig.KeyID]; !ok {
t.Fatalf("Got a signature we didn't expect: %s", sig.KeyID)
}
}
require.Equal(t, 2, count)
}
func TestSignReturnsNoSigs(t *testing.T) {
failingCryptoService := &FailingCryptoService{}
testData := data.Signed{}
testKey, _ := pem.Decode([]byte(testKeyPEM1))
key := data.NewPublicKey(data.RSAKey, testKey.Bytes)
err := Sign(failingCryptoService, &testData, key)
if err == nil {
t.Fatalf("Expected failure due to no signature being returned by the crypto service")
}
if len(testData.Signatures) != 0 {
t.Fatalf("Incorrect number of signatures, expected 0: %d", len(testData.Signatures))
}
}
func TestSignWithX509(t *testing.T) {
// generate a key becase we need a cert
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, 1024)
require.NoError(t, err)
// make a RSA x509 key
cert, err := cryptoservice.GenerateTestingCertificate(privKey.CryptoSigner(), "test")
require.NoError(t, err)
tufRSAx509Key := trustmanager.CertToKey(cert)
require.NoError(t, err)
// test signing against a service that only recognizes a RSAKey (not
// RSAx509 key)
mockCryptoService := &StrictMockCryptoService{MockCryptoService{privKey}}
testData := data.Signed{}
err = Sign(mockCryptoService, &testData, tufRSAx509Key)
require.NoError(t, err)
require.Len(t, testData.Signatures, 1)
require.Equal(t, tufRSAx509Key.ID(), testData.Signatures[0].KeyID)
}
func TestSignRemovesValidSigByInvalidKey(t *testing.T) {
cs := NewEd25519()
testData := data.Signed{}
key1, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
Sign(cs, &testData, key1)
require.Len(t, testData.Signatures, 1)
require.Equal(t, key1.ID(), testData.Signatures[0].KeyID)
key2, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
// should remove key1 sig even though it's valid. It no longer appears
// in the list of signing keys for the role
Sign(
cs,
&testData,
key2,
)
require.Len(t, testData.Signatures, 1)
require.Equal(t, key2.ID(), testData.Signatures[0].KeyID)
}
func TestSignRemovesInvalidSig(t *testing.T) {
cs := NewEd25519()
testData := data.Signed{}
key1, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
Sign(cs, &testData, key1)
require.Len(t, testData.Signatures, 1)
require.Equal(t, key1.ID(), testData.Signatures[0].KeyID)
// we need cs to "forget" key1 so we can't sign with it
cs = NewEd25519()
key2, err := cs.Create(data.CanonicalRootRole, data.ED25519Key)
require.NoError(t, err)
// modify test data to invalidate key1 sig
testData.Signed = []byte{0xff}
// should remove key1 sig because it's out of date
Sign(
cs,
&testData,
key1,
key2,
)
require.Len(t, testData.Signatures, 1)
require.Equal(t, key2.ID(), testData.Signatures[0].KeyID)
}