From bea9d25b203072ea90cb5016ea2dccb9d2866781 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Mon, 26 Sep 2016 11:02:06 -0400 Subject: [PATCH] Allow non-cert public keys Signed-off-by: Evan Cordell --- trustpinning/certs_test.go | 69 ++++++++++++++++++++++++++++++-------- tuf/utils/x509.go | 22 +++++++++++- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/trustpinning/certs_test.go b/trustpinning/certs_test.go index 99bffd7098..b90b773360 100644 --- a/trustpinning/certs_test.go +++ b/trustpinning/certs_test.go @@ -193,11 +193,7 @@ func TestValidateRootWithPinnedCert(t *testing.T) { func TestValidateRootWithPinnedCertAndIntermediates(t *testing.T) { now := time.Now() serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - - pass := func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) { - return "password", false, nil - } - memStore := trustmanager.NewKeyMemoryStore(pass) + memStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cs := cryptoservice.NewCryptoService(memStore) // generate CA cert @@ -1007,12 +1003,61 @@ func generateExpiredTestingCertificate(rootKey data.PrivateKey, gun string) (*x5 return cryptoservice.GenerateCertificate(rootKey, gun, startTime, startTime.AddDate(1, 0, 0)) } +// Helper function for explicitly generating key IDs and unexported fields for equality testing +func generateRootKeyIDs(r *data.SignedRoot) { + for _, keyID := range r.Signed.Roles[data.CanonicalRootRole].KeyIDs { + if k, ok := r.Signed.Keys[keyID]; ok { + _ = k.ID() + } + } +} + +func TestParsePEMPublicKey(t *testing.T) { + gun := "notary" + memStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) + cs := cryptoservice.NewCryptoService(memStore) + + // can parse ECDSA PEM + ecdsaPubKey, err := cs.Create("root", "docker.io/notary/test", data.ECDSAKey) + require.NoError(t, err) + ecdsaPemBytes := pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Headers: nil, + Bytes: ecdsaPubKey.Public(), + }) + + ecdsaParsedPubKey, err := utils.ParsePEMPublicKey(ecdsaPemBytes) + require.NoError(t, err, "no key: %s", ecdsaParsedPubKey.Public()) + + // can parse certificates + ecdsaPrivKey, _, err := memStore.GetKey(ecdsaPubKey.ID()) + require.NoError(t, err) + cert, err := generateTestingCertificate(ecdsaPrivKey, gun, notary.Day*30) + require.NoError(t, err) + ecdsaPubKeyFromCert, err := utils.ParsePEMPublicKey(utils.CertToPEM(cert)) + require.NoError(t, err) + + thatData := []byte{1, 2, 3, 4} + sig, err := ecdsaPrivKey.Sign(rand.Reader, thatData, nil) + require.NoError(t, err) + err = signed.ECDSAVerifier{}.Verify(ecdsaPubKeyFromCert, sig, thatData) + require.NoError(t, err) + + // can parse RSA PEM + rsaPubKey, err := cs.Create("root", "docker.io/notary/test2", data.RSAKey) + require.NoError(t, err) + rsaPemBytes := pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Headers: nil, + Bytes: rsaPubKey.Public(), + }) + _, err = utils.ParsePEMPublicKey(rsaPemBytes) + require.NoError(t, err) +} + func TestCheckingCertExpiry(t *testing.T) { gun := "notary" - pass := func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) { - return "password", false, nil - } - memStore := trustmanager.NewKeyMemoryStore(pass) + memStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cs := cryptoservice.NewCryptoService(memStore) testPubKey, err := cs.Create(data.CanonicalRootRole, gun, data.ECDSAKey) require.NoError(t, err) @@ -1088,11 +1133,7 @@ func TestCheckingCertExpiry(t *testing.T) { func TestValidateRootWithExpiredIntermediate(t *testing.T) { now := time.Now() serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - - pass := func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) { - return "password", false, nil - } - memStore := trustmanager.NewKeyMemoryStore(pass) + memStore := trustmanager.NewKeyMemoryStore(passphraseRetriever) cs := cryptoservice.NewCryptoService(memStore) // generate CA cert diff --git a/tuf/utils/x509.go b/tuf/utils/x509.go index 5d67002986..deca3c8482 100644 --- a/tuf/utils/x509.go +++ b/tuf/utils/x509.go @@ -252,9 +252,29 @@ func ParsePEMPublicKey(pubKeyBytes []byte) (data.PublicKey, error) { return nil, fmt.Errorf("invalid certificate: %v", err) } return CertToKey(cert), nil + case "PUBLIC KEY": + keyType, err := keyTypeForPublicKey(pemBlock.Bytes) + if err != nil { + return nil, err + } + return data.NewPublicKey(keyType, pemBlock.Bytes), nil default: - return nil, fmt.Errorf("unsupported PEM block type %q, expected certificate", pemBlock.Type) + return nil, fmt.Errorf("unsupported PEM block type %q, expected CERTIFICATE or PUBLIC KEY", pemBlock.Type) + } +} + +func keyTypeForPublicKey(pubKeyBytes []byte) (string, error) { + pub, err := x509.ParsePKIXPublicKey(pubKeyBytes) + if err != nil { + return "", fmt.Errorf("unable to parse pem encoded public key: %v", err) + } + switch pub.(type) { + case *ecdsa.PublicKey: + return data.ECDSAKey, nil + case *rsa.PublicKey: + return data.RSAKey, nil } + return "", fmt.Errorf("unknown public key format") } // ValidateCertificate returns an error if the certificate is not valid for notary