From ba94fdd19dd46ee2dd411e14aacf1964c35883ae Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Sun, 12 Jul 2015 16:19:13 -0700 Subject: [PATCH] Signature/key types are now used correcty and are represented by constants. Signed-off-by: Diogo Monica --- client/cli_crypto_service.go | 23 +++++------------- client/client.go | 46 ++++++++++++++++++++++++++--------- client/client_test.go | 30 +++++++++++++++++++---- cmd/notary/tuf.go | 46 ++++++++++++++++++++++++++++++++++- server/storage/memory_test.go | 11 +++++---- trustmanager/x509utils.go | 28 ++++++++++----------- 6 files changed, 131 insertions(+), 53 deletions(-) diff --git a/client/cli_crypto_service.go b/client/cli_crypto_service.go index 3ad2d347a6..c6ce56828c 100644 --- a/client/cli_crypto_service.go +++ b/client/cli_crypto_service.go @@ -9,7 +9,6 @@ import ( "crypto/x509" "fmt" "path/filepath" - "strings" "github.com/Sirupsen/logrus" "github.com/docker/notary/trustmanager" @@ -72,7 +71,6 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa var privKey *data.PrivateKey var err error - var method string // Read PrivateKey from file. // TODO(diogo): This assumes both that only root keys are encrypted and @@ -80,12 +78,8 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa if ccs.passphrase != "" { // This is a root key privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase) - // This method is added to the signature so verifiers can decode - // the X509 certificate before trying to parse the public materials - method = "RSASSA-PSS-X509" } else { privKey, err = ccs.keyStore.GetKey(keyName) - method = "RSASSA-PSS" } if err != nil { // Note that GetDecryptedKey always fails on InitRepo. @@ -107,12 +101,12 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa continue } - logrus.Debugf("appending RSA signature with Key ID: %s and method %s", privKey.ID(), method) + logrus.Debugf("appending RSA signature with Key ID: %s", privKey.ID()) // Append signatures to result array signatures = append(signatures, data.Signature{ KeyID: keyid, - Method: method, + Method: data.RSAPSSSignature, Signature: sig[:], }) } @@ -121,7 +115,7 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa } func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { - if strings.ToUpper(privKey.Cipher()) != "RSA" { + if privKey.Cipher() != data.RSAKey { return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher()) } @@ -178,7 +172,6 @@ func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sig var privKey *data.PrivateKey var err error - var method string // Read PrivateKey from file // TODO(diogo): This assumes both that only root keys are encrypted and @@ -186,12 +179,8 @@ func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sig if ccs.passphrase != "" { // This is a root key privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase) - // This method is added to the signature so verifiers can decode - // the X509 certificate before trying to parse the public materials - method = "ECDSA-X509" } else { privKey, err = ccs.keyStore.GetKey(keyName) - method = "ECDSA" } if err != nil { // Note that GetDecryptedKey always fails on InitRepo. @@ -218,12 +207,12 @@ func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sig continue } - logrus.Debugf("appending ECDSA signature with Key ID: %s and method: %s", privKey.ID(), method) + logrus.Debugf("appending ECDSA signature with Key ID: %s", privKey.ID()) // Append signatures to result array signatures = append(signatures, data.Signature{ KeyID: keyid, - Method: method, + Method: data.ECDSASignature, Signature: sig[:], }) } @@ -232,7 +221,7 @@ func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Sig } func ecdsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { - if strings.ToLower(privKey.Cipher()) != "ecdsa" { + if privKey.Cipher() != data.ECDSAKey { return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher()) } diff --git a/client/client.go b/client/client.go index 6f1b64305e..9cea571efb 100644 --- a/client/client.go +++ b/client/client.go @@ -15,6 +15,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "time" "github.com/Sirupsen/logrus" @@ -143,14 +144,35 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error { } r.certificateStore.AddCert(rootCert) - rootKey := data.NewPublicKey(uSigner.privKey.Cipher(), trustmanager.CertToPEM(rootCert)) - logrus.Debugf("Linking %s to %s.", rootKey.ID(), uSigner.ID()) + // The root key gets stored in the TUF metadata X509 encoded, linking + // the tuf root.json to our X509 PKI. + // If the key is RSA, we store it as type RSAx509, if it is ECDSA we store it + // as ECDSAx509 to allow the gotuf verifiers to correctly decode the + // key on verification of signatures. + var cipherType string + cipher := uSigner.privKey.Cipher() + switch cipher { + case data.RSAKey: + cipherType = data.RSAx509Key + case data.ECDSAKey: + cipherType = data.ECDSAx509Key + default: + return fmt.Errorf("invalid format for root key: %s", cipher) + } + // Generate a x509Key using the rootCert as the public key + rootKey := data.NewPublicKey(cipherType, trustmanager.CertToPEM(rootCert)) + + // Creates a symlink between the certificate ID and the real public key it + // is associated with. This is used to be able to retrieve the root private key + // associated with a particular certificate + logrus.Debugf("Linking %s to %s.", rootKey.ID(), uSigner.ID()) err = r.rootKeyStore.Link(uSigner.ID(), rootKey.ID()) if err != nil { return err } + // All the timestamp keys are generated by the remote server. remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip) rawTSKey, err := remote.GetKey("timestamp") if err != nil { @@ -163,10 +185,11 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error { return err } + // Turn the JSON timestamp key from the remote server into a TUFKey timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public()) logrus.Debugf("got remote %s timestamp key with keyID: %s", parsedKey.Cipher(), timestampKey.ID()) - logrus.Debugf("timestamp key: %s", rawTSKey) + // Targets and snapshot keys are always generated locally. targetsKey, err := r.signer.Create("targets") if err != nil { return err @@ -634,10 +657,10 @@ func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, err var err error var privKey *data.PrivateKey - switch algorithm { - case "RSA": + switch strings.ToLower(algorithm) { + case data.RSAKey: privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize) - case "ECDSA": + case data.ECDSAKey: privKey, err = trustmanager.GenerateECDSAKey(rand.Reader) default: return "", fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm) @@ -647,6 +670,7 @@ func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, err return "", fmt.Errorf("failed to generate private key: %v", err) } + // Changing the root r.rootKeyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase) return privKey.ID(), nil @@ -662,10 +686,10 @@ func (r *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*Unlocke var signer *signed.Signer cipher := privKey.Cipher() // Passing an empty GUN because root keys aren't associated with a GUN. - switch cipher { - case "RSA": + switch strings.ToLower(cipher) { + case data.RSAKey: signer = signed.NewSigner(NewRSACryptoService("", r.rootKeyStore, passphrase)) - case "ECDSA": + case data.ECDSAKey: signer = signed.NewSigner(NewECDSACryptoService("", r.rootKeyStore, passphrase)) default: return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher) @@ -731,12 +755,12 @@ func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, er var privateKey crypto.PrivateKey var err error switch cipher { - case "RSA": + case data.RSAKey: var rsaPrivateKey *rsa.PrivateKey rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(uk.privKey.Private()) privateKey = rsaPrivateKey publicKey = rsaPrivateKey.Public() - case "ECDSA": + case data.ECDSAKey: var ecdsaPrivateKey *ecdsa.PrivateKey ecdsaPrivateKey, err = x509.ParseECPrivateKey(uk.privKey.Private()) privateKey = ecdsaPrivateKey diff --git a/client/client_test.go b/client/client_test.go index 1e47d4168f..084c560e72 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -17,9 +17,12 @@ import ( "github.com/stretchr/testify/assert" ) -const timestampKeyJSON = `{"keytype":"RSA","keyval":{"public":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQAB","private":"MIIEpAIBAAKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQABAoIBAHar8FFxrE1gAGTeUpOF8fG8LIQMRwO4U6eVY7V9GpWiv6gOJTHXYFxU/aL0Ty3eQRxwy9tyVRo8EJz5pRex+e6ws1M+jLOviYqW4VocxQ8dZYd+zBvQfWmRfah7XXJ/HPUx2I05zrmR7VbGX6Bu4g5w3KnyIO61gfyQNKF2bm2Q3yblfupx3URvX0bl180R/+QN2Aslr4zxULFE6b+qJqBydrztq+AAP3WmskRxGa6irFnKxkspJqUpQN1mFselj6iQrzAcwkRPoCw0RwCCMq1/OOYvQtgxTJcO4zDVlbw54PvnxPZtcCWw7fO8oZ2Fvo2SDo75CDOATOGaT4Y9iqECgYEAzWZSpFbN9ZHmvq1lJQg//jFAyjsXRNn/nSvyLQILXltz6EHatImnXo3v+SivG91tfzBI1GfDvGUGaJpvKHoomB+qmhd8KIQhO5MBdAKZMf9fZqZofOPTD9xRXECCwdi+XqHBmL+l1OWz+O9Bh+Qobs2as/hQVgHaoXhQpE0NkTcCgYEA/Tjf6JBGl1+WxQDoGZDJrXoejzG9OFW19RjMdmPrg3t4fnbDtqTpZtCzXxPTCSeMrvplKbqAqZglWyq227ksKw4p7O6YfyhdtvC58oJmivlLr6sFaTsER7mDcYce8sQpqm+XQ8IPbnOk0Z1l6g56euTwTnew49uy25M6U1xL0P8CgYEAxEXv2Kw+OVhHV5PX4BBHHj6we88FiDyMfwM8cvfOJ0datekf9X7ImZkmZEAVPJpWBMD+B0J0jzU2b4SLjfFVkzBHVOH2Ob0xCH2MWPAWtekin7OKizUlPbW5ZV8b0+Kq30DQ/4a7D3rEhK8UPqeuX1tHZox1MAqrgbq3zJj4yvcCgYEAktYPKPm4pYCdmgFrlZ+bA0iEPf7Wvbsd91F5BtHsOOM5PQQ7e0bnvWIaEXEad/2CG9lBHlBy2WVLjDEZthILpa/h6e11ao8KwNGY0iKBuebT17rxOVMqqTjPGt8CuD2994IcEgOPFTpkAdUmyvG4XlkxbB8F6St17NPUB5DGuhsCgYA//Lfytk0FflXEeRQ16LT1YXgV7pcR2jsha4+4O5pxSFw/kTsOfJaYHg8StmROoyFnyE3sg76dCgLn0LENRCe5BvDhJnp5bMpQldG3XwcAxH8FGFNY4LtV/2ZKnJhxcONkfmzQPOmTyedOzrKQ+bNURsqLukCypP7/by6afBY4dA=="}}` +// TODO(diogo): timestamps have to be the same keytype as targets and snapshots. +// Can't test an RSA timestamp key until we stop hardcoding ECDSA as the default +// CryptoService in NewNotaryRepository +const timestampKeyJSON = `{"keytype":"rsa","keyval":{"public":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQAB","private":"MIIEpAIBAAKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQABAoIBAHar8FFxrE1gAGTeUpOF8fG8LIQMRwO4U6eVY7V9GpWiv6gOJTHXYFxU/aL0Ty3eQRxwy9tyVRo8EJz5pRex+e6ws1M+jLOviYqW4VocxQ8dZYd+zBvQfWmRfah7XXJ/HPUx2I05zrmR7VbGX6Bu4g5w3KnyIO61gfyQNKF2bm2Q3yblfupx3URvX0bl180R/+QN2Aslr4zxULFE6b+qJqBydrztq+AAP3WmskRxGa6irFnKxkspJqUpQN1mFselj6iQrzAcwkRPoCw0RwCCMq1/OOYvQtgxTJcO4zDVlbw54PvnxPZtcCWw7fO8oZ2Fvo2SDo75CDOATOGaT4Y9iqECgYEAzWZSpFbN9ZHmvq1lJQg//jFAyjsXRNn/nSvyLQILXltz6EHatImnXo3v+SivG91tfzBI1GfDvGUGaJpvKHoomB+qmhd8KIQhO5MBdAKZMf9fZqZofOPTD9xRXECCwdi+XqHBmL+l1OWz+O9Bh+Qobs2as/hQVgHaoXhQpE0NkTcCgYEA/Tjf6JBGl1+WxQDoGZDJrXoejzG9OFW19RjMdmPrg3t4fnbDtqTpZtCzXxPTCSeMrvplKbqAqZglWyq227ksKw4p7O6YfyhdtvC58oJmivlLr6sFaTsER7mDcYce8sQpqm+XQ8IPbnOk0Z1l6g56euTwTnew49uy25M6U1xL0P8CgYEAxEXv2Kw+OVhHV5PX4BBHHj6we88FiDyMfwM8cvfOJ0datekf9X7ImZkmZEAVPJpWBMD+B0J0jzU2b4SLjfFVkzBHVOH2Ob0xCH2MWPAWtekin7OKizUlPbW5ZV8b0+Kq30DQ/4a7D3rEhK8UPqeuX1tHZox1MAqrgbq3zJj4yvcCgYEAktYPKPm4pYCdmgFrlZ+bA0iEPf7Wvbsd91F5BtHsOOM5PQQ7e0bnvWIaEXEad/2CG9lBHlBy2WVLjDEZthILpa/h6e11ao8KwNGY0iKBuebT17rxOVMqqTjPGt8CuD2994IcEgOPFTpkAdUmyvG4XlkxbB8F6St17NPUB5DGuhsCgYA//Lfytk0FflXEeRQ16LT1YXgV7pcR2jsha4+4O5pxSFw/kTsOfJaYHg8StmROoyFnyE3sg76dCgLn0LENRCe5BvDhJnp5bMpQldG3XwcAxH8FGFNY4LtV/2ZKnJhxcONkfmzQPOmTyedOzrKQ+bNURsqLukCypP7/by6afBY4dA=="}}` const timestampECDSAKeyJSON = ` -{"keytype":"ECDSA","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}` +{"keytype":"ecdsa","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}` func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { mux := http.NewServeMux() @@ -38,7 +41,13 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { // TestInitRepo runs through the process of initializing a repository and makes // sure the repository looks correct on disk. +// We test this with both an RSA and ECDSA root key func TestInitRepo(t *testing.T) { + testInitRepo(t, data.RSAKey) + testInitRepo(t, data.ECDSAKey) +} + +func testInitRepo(t *testing.T, rootType string) { gun := "docker.com/notary" // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") @@ -52,7 +61,7 @@ func TestInitRepo(t *testing.T) { repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) assert.NoError(t, err, "error creating repo: %s", err) - rootKeyID, err := repo.GenRootKey("ECDSA", "passphrase") + rootKeyID, err := repo.GenRootKey(rootType, "passphrase") assert.NoError(t, err, "error generating root key: %s", err) rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase") @@ -173,7 +182,13 @@ type tufChange struct { // is updated correctly. Then it calls ListTargets and checks the return value. // Using ListTargets involves serving signed metadata files over the test's // internal HTTP server. +// We test this with both an RSA and ECDSA root key func TestAddListTarget(t *testing.T) { + testAddListTarget(t, data.RSAKey) + testAddListTarget(t, data.ECDSAKey) +} + +func testAddListTarget(t *testing.T, rootType string) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) @@ -188,7 +203,7 @@ func TestAddListTarget(t *testing.T) { repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) assert.NoError(t, err, "error creating repository: %s", err) - rootKeyID, err := repo.GenRootKey("ECDSA", "passphrase") + rootKeyID, err := repo.GenRootKey(rootType, "passphrase") assert.NoError(t, err, "error generating root key: %s", err) rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase") @@ -352,6 +367,11 @@ func TestAddListTarget(t *testing.T) { // TestValidateRootKey verifies that the public data in root.json for the root // key is a valid x509 certificate. func TestValidateRootKey(t *testing.T) { + testValidateRootKey(t, data.RSAKey) + testValidateRootKey(t, data.ECDSAKey) +} + +func testValidateRootKey(t *testing.T, rootType string) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) @@ -366,7 +386,7 @@ func TestValidateRootKey(t *testing.T) { repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) assert.NoError(t, err, "error creating repository: %s", err) - rootKeyID, err := repo.GenRootKey("ECDSA", "passphrase") + rootKeyID, err := repo.GenRootKey(rootType, "passphrase") assert.NoError(t, err, "error generating root key: %s", err) rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase") diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index 8c8e64495e..2d518a5a1a 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -1,6 +1,8 @@ package main import ( + "bufio" + "bytes" "crypto/sha256" "errors" "fmt" @@ -9,7 +11,9 @@ import ( "os" "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/term" notaryclient "github.com/docker/notary/client" + "github.com/endophage/gotuf/data" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -116,7 +120,7 @@ func tufInit(cmd *cobra.Command, args []string) { if err != nil { fatalf(err.Error()) } - rootKeyID, err = nRepo.GenRootKey("ECDSA", passphrase) + rootKeyID, err = nRepo.GenRootKey(data.ECDSAKey, passphrase) if err != nil { fatalf(err.Error()) } @@ -279,3 +283,43 @@ func passphraseRetriever() (string, error) { } return passphrase, nil } + +func getPassphrase(confirm bool) ([]byte, error) { + if pass := os.Getenv("NOTARY_ROOT_PASSPHRASE"); pass != "" { + return []byte(pass), nil + } + + state, err := term.SaveState(0) + if err != nil { + return nil, err + } + term.DisableEcho(0, state) + defer term.RestoreTerminal(0, state) + + stdin := bufio.NewReader(os.Stdin) + + fmt.Printf("Enter root key passphrase: ") + passphrase, err := stdin.ReadBytes('\n') + fmt.Println() + if err != nil { + return nil, err + } + passphrase = passphrase[0 : len(passphrase)-1] + + if !confirm { + return passphrase, nil + } + + fmt.Printf("Repeat root key passphrase: ") + confirmation, err := stdin.ReadBytes('\n') + fmt.Println() + if err != nil { + return nil, err + } + confirmation = confirmation[0 : len(confirmation)-1] + + if !bytes.Equal(passphrase, confirmation) { + return nil, errors.New("The entered passphrases do not match") + } + return passphrase, nil +} diff --git a/server/storage/memory_test.go b/server/storage/memory_test.go index 0040b5b04d..c6a7f00d53 100644 --- a/server/storage/memory_test.go +++ b/server/storage/memory_test.go @@ -3,6 +3,7 @@ package storage import ( "testing" + "github.com/endophage/gotuf/data" "github.com/stretchr/testify/assert" ) @@ -46,23 +47,23 @@ func TestGetTimestampKey(t *testing.T) { //_, _, err := s.GetTimestampKey("gun") //assert.IsType(t, &ErrNoKey{}, err, "Expected err to be ErrNoKey") - s.SetTimestampKey("gun", "RSA", []byte("test")) + s.SetTimestampKey("gun", data.RSAKey, []byte("test")) c, k, err := s.GetTimestampKey("gun") assert.Nil(t, err, "Expected error to be nil") - assert.Equal(t, "RSA", c, "Expected cipher rsa, received %s", c) + assert.Equal(t, data.RSAKey, c, "Expected cipher rsa, received %s", c) assert.Equal(t, []byte("test"), k, "Key data was wrong") } func TestSetTimestampKey(t *testing.T) { s := NewMemStorage() - s.SetTimestampKey("gun", "RSA", []byte("test")) + s.SetTimestampKey("gun", data.RSAKey, []byte("test")) - err := s.SetTimestampKey("gun", "RSA", []byte("test2")) + err := s.SetTimestampKey("gun", data.RSAKey, []byte("test2")) assert.IsType(t, &ErrTimestampKeyExists{}, err, "Expected err to be ErrTimestampKeyExists") k := s.tsKeys["gun"] - assert.Equal(t, "RSA", k.cipher, "Expected cipher to be rsa, received %s", k.cipher) + assert.Equal(t, data.RSAKey, k.cipher, "Expected cipher to be rsa, received %s", k.cipher) assert.Equal(t, []byte("test"), k.public, "Public key did not match expected") } diff --git a/trustmanager/x509utils.go b/trustmanager/x509utils.go index 1b621e9a81..928dd3d8b3 100644 --- a/trustmanager/x509utils.go +++ b/trustmanager/x509utils.go @@ -103,9 +103,9 @@ func fingerprintCert(cert *x509.Certificate) (CertID, error) { keyType := "" switch cert.PublicKeyAlgorithm { case x509.RSA: - keyType = "RSA" + keyType = data.RSAx509Key case x509.ECDSA: - keyType = "ECDSA" + keyType = data.ECDSAx509Key default: return "", fmt.Errorf("error while fingerprinting certificate. Got Unknown key type.") } @@ -174,7 +174,7 @@ func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (*data.PrivateKey, e return nil, fmt.Errorf("could not parse DER encoded key: %v", err) } - tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey) + tufRSAPrivateKey, err := RSAToPrivateKey(rsaPrivKey, data.RSAKey) if err != nil { return nil, fmt.Errorf("could not convert rsa.PrivateKey to data.PrivateKey: %v", err) } @@ -198,7 +198,7 @@ func ParsePEMPrivateKey(pemBytes []byte, passphrase string) (*data.PrivateKey, e return nil, fmt.Errorf("could not parse DER encoded private key: %v", err) } - tufRSAPrivateKey, err := ECDSAToPrivateKey(rsaPrivKey) + tufRSAPrivateKey, err := ECDSAToPrivateKey(rsaPrivKey, data.ECDSAKey) if err != nil { return nil, fmt.Errorf("could not convert ecdsa.PrivateKey to data.PrivateKey: %v", err) } @@ -217,7 +217,7 @@ func GenerateRSAKey(random io.Reader, bits int) (*data.PrivateKey, error) { return nil, fmt.Errorf("could not generate private key: %v", err) } - tufPrivKey, err := RSAToPrivateKey(rsaPrivKey) + tufPrivKey, err := RSAToPrivateKey(rsaPrivKey, data.RSAKey) if err != nil { return nil, err } @@ -228,7 +228,7 @@ func GenerateRSAKey(random io.Reader, bits int) (*data.PrivateKey, error) { } // RSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type -func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (*data.PrivateKey, error) { +func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey, keyType string) (*data.PrivateKey, error) { // Get a DER-encoded representation of the PublicKey rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey) if err != nil { @@ -238,7 +238,7 @@ func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (*data.PrivateKey, error) { // Get a DER-encoded representation of the PrivateKey rsaPrivBytes := x509.MarshalPKCS1PrivateKey(rsaPrivKey) - return data.NewPrivateKey("RSA", rsaPubBytes, rsaPrivBytes), nil + return data.NewPrivateKey(keyType, rsaPubBytes, rsaPrivBytes), nil } // GenerateECDSAKey generates an ECDSA Private key and returns a TUF PrivateKey @@ -250,7 +250,7 @@ func GenerateECDSAKey(random io.Reader) (*data.PrivateKey, error) { return nil, err } - tufPrivKey, err := ECDSAToPrivateKey(ecdsaPrivKey) + tufPrivKey, err := ECDSAToPrivateKey(ecdsaPrivKey, data.ECDSAKey) if err != nil { return nil, err } @@ -261,7 +261,7 @@ func GenerateECDSAKey(random io.Reader) (*data.PrivateKey, error) { } // ECDSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type -func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey) (*data.PrivateKey, error) { +func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey, keyType string) (*data.PrivateKey, error) { // Get a DER-encoded representation of the PublicKey ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey) if err != nil { @@ -274,7 +274,7 @@ func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey) (*data.PrivateKey, error) return nil, fmt.Errorf("failed to marshal private key: %v", err) } - return data.NewPrivateKey("ECDSA", ecdsaPubBytes, ecdsaPrivKeyBytes), nil + return data.NewPrivateKey(keyType, ecdsaPubBytes, ecdsaPrivKeyBytes), nil } // KeyToPEM returns a PEM encoded key from a Private Key @@ -283,9 +283,9 @@ func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) { cipher := privKey.Cipher() switch cipher { - case "RSA": + case data.RSAKey: pemType = "RSA PRIVATE KEY" - case "ECDSA": + case data.ECDSAKey: pemType = "EC PRIVATE KEY" default: return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher) @@ -301,9 +301,9 @@ func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) cipher := key.Cipher() switch cipher { - case "RSA": + case data.RSAKey: blockType = "RSA PRIVATE KEY" - case "ECDSA": + case data.ECDSAKey: blockType = "EC PRIVATE KEY" default: return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)