diff --git a/client/cli_crypto_service.go b/client/cli_crypto_service.go index 3b9aa8a3f5..72ba2ef7a4 100644 --- a/client/cli_crypto_service.go +++ b/client/cli_crypto_service.go @@ -15,51 +15,52 @@ import ( "github.com/endophage/gotuf/data" ) -type genericCryptoService struct { +// CryptoService implements Sign and Create, holding a specific GUN and keystore to +// operate on +type CryptoService struct { gun string passphrase string keyStore *trustmanager.KeyFileStore } -// RSACryptoService implements Sign and Create, holding a specific GUN and keystore to -// operate on -type RSACryptoService struct { - genericCryptoService -} - -// ECDSACryptoService implements Sign and Create, holding a specific GUN and keystore to -// operate on -type ECDSACryptoService struct { - genericCryptoService -} - -// NewRSACryptoService returns an instance of CryptoService -func NewRSACryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *RSACryptoService { - return &RSACryptoService{genericCryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}} +// NewCryptoService returns an instance of CryptoService +func NewCryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *CryptoService { + return &CryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase} } // Create is used to generate keys for targets, snapshots and timestamps -func (ccs *RSACryptoService) Create(role string) (*data.PublicKey, error) { - privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize) - if err != nil { - return nil, fmt.Errorf("failed to generate RSA key: %v", err) +func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) { + var privKey *data.PrivateKey + var err error + + switch algorithm { + case data.RSAKey: + privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize) + if err != nil { + return nil, fmt.Errorf("failed to generate RSA key: %v", err) + } + case data.ECDSAKey: + privKey, err = trustmanager.GenerateECDSAKey(rand.Reader) + if err != nil { + return nil, fmt.Errorf("failed to generate EC key: %v", err) + } + default: + return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm) } + logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID()) // Store the private key into our keystore with the name being: /GUN/ID.key err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey) if err != nil { return nil, fmt.Errorf("failed to add key to filestore: %v", err) } - - logrus.Debugf("generated new RSA key for role: %s and keyID: %s", role, privKey.ID()) - return data.PublicKeyFromPrivate(*privKey), nil } // Sign returns the signatures for the payload with a set of keyIDs. It ignores // errors to sign and expects the called to validate if the number of returned // signatures is adequate. -func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { +func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { // Create hasher and hash data hash := crypto.SHA256 hashed := sha256.Sum256(payload) @@ -72,11 +73,8 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa var privKey *data.PrivateKey var err error - // Read PrivateKey from file. - // TODO(diogo): This assumes both that only root keys are encrypted and - // that encrypted keys are always X509 encoded + // Read PrivateKey from file and decrypt it if there is a passphrase. if ccs.passphrase != "" { - // This is a root key privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase) } else { privKey, err = ccs.keyStore.GetKey(keyName) @@ -86,27 +84,33 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa // InitRepo gets a signer that doesn't have access to // the root keys. Continuing here is safe because we // end up not returning any signatures. - logrus.Debugf("ignoring error attempting to retrieve RSA key ID: %s, %v", keyid, err) + logrus.Debugf("ignoring error attempting to retrieve key ID: %s, %v", keyid, err) continue } - sig, err := rsaSign(privKey, hash, hashed[:]) + algorithm := privKey.Algorithm() + var sigAlgorithm data.SigAlgorithm + var sig []byte + + switch algorithm { + case data.RSAKey: + sig, err = rsaSign(privKey, hash, hashed[:]) + sigAlgorithm = data.RSAPSSSignature + case data.ECDSAKey: + sig, err = ecdsaSign(privKey, hashed[:]) + sigAlgorithm = data.ECDSASignature + } if err != nil { - // If the rsaSign method got called with a non RSA private key, - // we ignore this call. - // This might happen when root is ECDSA, targets and snapshots RSA, and - // gotuf still attempts to sign root with this cryptoserver - // return nil, err - logrus.Debugf("ignoring error attempting to RSA sign with keyID: %s, %v", keyid, err) + logrus.Debugf("ignoring error attempting to %s sign with keyID: %s, %v", algorithm, keyid, err) continue } - logrus.Debugf("appending RSA signature with Key ID: %s", privKey.ID()) + logrus.Debugf("appending %s signature with Key ID: %s", algorithm, keyid) // Append signatures to result array signatures = append(signatures, data.Signature{ KeyID: keyid, - Method: data.RSAPSSSignature, + Method: sigAlgorithm, Signature: sig[:], }) } @@ -115,8 +119,8 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa } func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { - if privKey.Cipher() != data.RSAKey { - return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher()) + if privKey.Algorithm() != data.RSAKey { + return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm()) } // Create an rsa.PrivateKey out of the private key bytes @@ -134,94 +138,9 @@ func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, return sig, nil } -// NewECDSACryptoService returns an instance of CryptoService -func NewECDSACryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *ECDSACryptoService { - return &ECDSACryptoService{genericCryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}} -} - -// Create is used to generate keys for targets, snapshots and timestamps -func (ccs *ECDSACryptoService) Create(role string) (*data.PublicKey, error) { - privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) - if err != nil { - return nil, fmt.Errorf("failed to generate EC key: %v", err) - } - - // Store the private key into our keystore with the name being: /GUN/ID.key - err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey) - if err != nil { - return nil, fmt.Errorf("failed to add key to filestore: %v", err) - } - - logrus.Debugf("generated new ECDSA key for role %s with keyID: %s", role, privKey.ID()) - - return data.PublicKeyFromPrivate(*privKey), nil -} - -// Sign returns the signatures for the payload with a set of keyIDs. It ignores -// errors to sign and expects the called to validate if the number of returned -// signatures is adequate. -func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) { - // Create hasher and hash data - hashed := sha256.Sum256(payload) - - signatures := make([]data.Signature, 0, len(keyIDs)) - for _, keyid := range keyIDs { - // ccs.gun will be empty if this is the root key - keyName := filepath.Join(ccs.gun, keyid) - - var privKey *data.PrivateKey - var err error - - // Read PrivateKey from file - // TODO(diogo): This assumes both that only root keys are encrypted and - // that encrypted keys are always X509 encoded - if ccs.passphrase != "" { - // This is a root key - privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase) - } else { - privKey, err = ccs.keyStore.GetKey(keyName) - } - if err != nil { - // Note that GetDecryptedKey always fails on InitRepo. - // InitRepo gets a signer that doesn't have access to - // the root keys. Continuing here is safe because we - // end up not returning any signatures. - // TODO(diogo): figure out if there are any specific error types to - // check. We're swallowing all errors. - logrus.Debugf("Ignoring error attempting to retrieve ECDSA key ID: %s, %v", keyid, err) - continue - } - if err != nil { - fmt.Println("ERROR: ", err.Error()) - } - - sig, err := ecdsaSign(privKey, hashed[:]) - if err != nil { - // If the ecdsaSign method got called with a non ECDSA private key, - // we ignore this call. - // This might happen when root is RSA, targets and snapshots ECDSA, and - // gotuf still attempts to sign root with this cryptoserver - // return nil, err - logrus.Debugf("ignoring error attempting to ECDSA sign with keyID: %s, %v", privKey.ID(), err) - continue - } - - logrus.Debugf("appending ECDSA signature with Key ID: %s", privKey.ID()) - - // Append signatures to result array - signatures = append(signatures, data.Signature{ - KeyID: keyid, - Method: data.ECDSASignature, - Signature: sig[:], - }) - } - - return signatures, nil -} - func ecdsaSign(privKey *data.PrivateKey, hashed []byte) ([]byte, error) { - if privKey.Cipher() != data.ECDSAKey { - return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher()) + if privKey.Algorithm() != data.ECDSAKey { + return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm()) } // Create an ecdsa.PrivateKey out of the private key bytes diff --git a/client/client.go b/client/client.go index 9cea571efb..7431280d0b 100644 --- a/client/client.go +++ b/client/client.go @@ -73,6 +73,7 @@ type NotaryRepository struct { certificateStore trustmanager.X509Store fileStore store.MetadataStore signer *signed.Signer + cryptoService signed.CryptoService tufRepo *tuf.TufRepo privKeyStore *trustmanager.KeyFileStore rootKeyStore *trustmanager.KeyFileStore @@ -115,17 +116,18 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N return nil, err } - // TODO(diogo): This hardcodes snapshots and targets to using EC. Change it. - signer := signed.NewSigner(NewECDSACryptoService(gun, privKeyStore, "")) + cryptoService := NewCryptoService(gun, privKeyStore, "") + signer := signed.NewSigner(cryptoService) nRepo := &NotaryRepository{ - gun: gun, - baseDir: baseDir, - baseURL: baseURL, - tufRepoPath: filepath.Join(baseDir, tufDir, gun), - signer: signer, - privKeyStore: privKeyStore, - roundTrip: rt, + gun: gun, + baseDir: baseDir, + baseURL: baseURL, + tufRepoPath: filepath.Join(baseDir, tufDir, gun), + signer: signer, + cryptoService: cryptoService, + privKeyStore: privKeyStore, + roundTrip: rt, } if err := nRepo.loadKeys(trustDir, rootKeysDir); err != nil { @@ -149,19 +151,19 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error { // 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 { + var algorithmType data.KeyAlgorithm + algorithm := uSigner.privKey.Algorithm() + switch algorithm { case data.RSAKey: - cipherType = data.RSAx509Key + algorithmType = data.RSAx509Key case data.ECDSAKey: - cipherType = data.ECDSAx509Key + algorithmType = data.ECDSAx509Key default: - return fmt.Errorf("invalid format for root key: %s", cipher) + return fmt.Errorf("invalid format for root key: %s", algorithm) } // Generate a x509Key using the rootCert as the public key - rootKey := data.NewPublicKey(cipherType, trustmanager.CertToPEM(rootCert)) + rootKey := data.NewPublicKey(algorithmType, 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 @@ -186,15 +188,16 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error { } // 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()) + timestampKey := data.NewPublicKey(parsedKey.Algorithm(), parsedKey.Public()) + logrus.Debugf("got remote %s timestamp key with keyID: %s", parsedKey.Algorithm(), timestampKey.ID()) + // This is currently hardcoding the targets and snapshots keys to ECDSA // Targets and snapshot keys are always generated locally. - targetsKey, err := r.signer.Create("targets") + targetsKey, err := r.cryptoService.Create("targets", data.ECDSAKey) if err != nil { return err } - snapshotKey, err := r.signer.Create("snapshot") + snapshotKey, err := r.cryptoService.Create("snapshot", data.ECDSAKey) if err != nil { return err } @@ -652,12 +655,16 @@ func (r *NotaryRepository) ListRootKeys() []string { return r.rootKeyStore.ListKeys() } +// TODO(diogo): show not create keys manually, should use a cryptoservice instead // GenRootKey generates a new root key protected by a given passphrase func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, error) { var err error var privKey *data.PrivateKey - switch strings.ToLower(algorithm) { + // We don't want external API callers to rely on internal TUF data types, so + // the API here should continue to receive a string algorithm, and ensure + // that it is downcased + switch data.KeyAlgorithm(strings.ToLower(algorithm)) { case data.RSAKey: privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize) case data.ECDSAKey: @@ -683,17 +690,7 @@ func (r *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*Unlocke return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err) } - var signer *signed.Signer - cipher := privKey.Cipher() - // Passing an empty GUN because root keys aren't associated with a GUN. - switch strings.ToLower(cipher) { - case data.RSAKey: - signer = signed.NewSigner(NewRSACryptoService("", r.rootKeyStore, passphrase)) - 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) - } + signer := signed.NewSigner(NewCryptoService("", r.rootKeyStore, passphrase)) return &UnlockedSigner{ privKey: privKey, @@ -738,7 +735,7 @@ func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error { return nil } -// ID gets a consistent ID based on the PrivateKey bytes and cipher type +// ID gets a consistent ID based on the PrivateKey bytes and algorithm type func (uk *UnlockedSigner) ID() string { return uk.PublicKey().ID() } @@ -750,11 +747,11 @@ func (uk *UnlockedSigner) PublicKey() *data.PublicKey { // GenerateCertificate generates an X509 Certificate from a template, given a GUN func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, error) { - cipher := uk.privKey.Cipher() + algorithm := uk.privKey.Algorithm() var publicKey crypto.PublicKey var privateKey crypto.PrivateKey var err error - switch cipher { + switch algorithm { case data.RSAKey: var rsaPrivateKey *rsa.PrivateKey rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(uk.privKey.Private()) @@ -766,7 +763,7 @@ func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, er privateKey = ecdsaPrivateKey publicKey = ecdsaPrivateKey.Public() default: - return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher) + return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm) } if err != nil { return nil, fmt.Errorf("failed to parse root key: %s (%v)", gun, err) diff --git a/client/client_test.go b/client/client_test.go index 084c560e72..d3ee9e3a21 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -43,11 +43,13 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { // 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) + if !testing.Short() { + testInitRepo(t, data.RSAKey) + } } -func testInitRepo(t *testing.T, rootType string) { +func testInitRepo(t *testing.T, rootType data.KeyAlgorithm) { gun := "docker.com/notary" // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") @@ -61,7 +63,7 @@ func testInitRepo(t *testing.T, rootType string) { repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) assert.NoError(t, err, "error creating repo: %s", err) - rootKeyID, err := repo.GenRootKey(rootType, "passphrase") + rootKeyID, err := repo.GenRootKey(rootType.String(), "passphrase") assert.NoError(t, err, "error generating root key: %s", err) rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase") @@ -184,11 +186,13 @@ type tufChange struct { // 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) + if !testing.Short() { + testInitRepo(t, data.RSAKey) + } } -func testAddListTarget(t *testing.T, rootType string) { +func testAddListTarget(t *testing.T, rootType data.KeyAlgorithm) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) @@ -203,7 +207,7 @@ func testAddListTarget(t *testing.T, rootType string) { repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) assert.NoError(t, err, "error creating repository: %s", err) - rootKeyID, err := repo.GenRootKey(rootType, "passphrase") + rootKeyID, err := repo.GenRootKey(rootType.String(), "passphrase") assert.NoError(t, err, "error generating root key: %s", err) rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase") @@ -367,11 +371,13 @@ func testAddListTarget(t *testing.T, rootType string) { // 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) + if !testing.Short() { + testInitRepo(t, data.RSAKey) + } } -func testValidateRootKey(t *testing.T, rootType string) { +func testValidateRootKey(t *testing.T, rootType data.KeyAlgorithm) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) @@ -386,7 +392,7 @@ func testValidateRootKey(t *testing.T, rootType string) { repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport) assert.NoError(t, err, "error creating repository: %s", err) - rootKeyID, err := repo.GenRootKey(rootType, "passphrase") + rootKeyID, err := repo.GenRootKey(rootType.String(), "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 2d518a5a1a..6284fec9bf 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -13,7 +13,6 @@ import ( "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" ) @@ -120,7 +119,7 @@ func tufInit(cmd *cobra.Command, args []string) { if err != nil { fatalf(err.Error()) } - rootKeyID, err = nRepo.GenRootKey(data.ECDSAKey, passphrase) + rootKeyID, err = nRepo.GenRootKey("ECDSA", passphrase) if err != nil { fatalf(err.Error()) } diff --git a/server/handlers/default.go b/server/handlers/default.go index 3e0cf1572f..3949d75f7e 100644 --- a/server/handlers/default.go +++ b/server/handlers/default.go @@ -224,7 +224,7 @@ func GetTimestampKeyHandler(ctx context.Context, w http.ResponseWriter, r *http. vars := mux.Vars(r) gun := vars["imageName"] - key, err := timestamp.GetOrCreateTimestampKey(gun, store, crypto) + key, err := timestamp.GetOrCreateTimestampKey(gun, store, crypto, data.ECDSAKey) if err != nil { return &errors.HTTPError{ HTTPStatus: http.StatusInternalServerError, diff --git a/server/storage/database.go b/server/storage/database.go index c3cdd554d6..6c7ea3ad4d 100644 --- a/server/storage/database.go +++ b/server/storage/database.go @@ -3,6 +3,7 @@ package storage import ( "database/sql" + "github.com/endophage/gotuf/data" "github.com/go-sql-driver/mysql" ) @@ -91,10 +92,11 @@ func (db *MySQLStorage) Delete(gun string) error { } // GetTimestampKey returns the timestamps Public Key data -func (db *MySQLStorage) GetTimestampKey(gun string) (cipher string, public []byte, err error) { +func (db *MySQLStorage) GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error) { stmt := "SELECT `cipher`, `public` FROM `timestamp_keys` WHERE `gun`=?;" row := db.QueryRow(stmt, gun) + var cipher string err = row.Scan(&cipher, &public) if err == sql.ErrNoRows { return "", nil, ErrNoKey{gun: gun} @@ -102,13 +104,13 @@ func (db *MySQLStorage) GetTimestampKey(gun string) (cipher string, public []byt return "", nil, err } - return cipher, public, err + return data.KeyAlgorithm(cipher), public, err } // SetTimestampKey attempts to write a TimeStamp key and returns an error if it already exists -func (db *MySQLStorage) SetTimestampKey(gun, cipher string, public []byte) error { +func (db *MySQLStorage) SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error { stmt := "INSERT INTO `timestamp_keys` (`gun`, `cipher`, `public`) VALUES (?,?,?);" - _, err := db.Exec(stmt, gun, cipher, public) + _, err := db.Exec(stmt, gun, string(algorithm), public) if err, ok := err.(*mysql.MySQLError); ok { if err.Number == 1022 { // duplicate key error return &ErrTimestampKeyExists{gun: gun} diff --git a/server/storage/interface.go b/server/storage/interface.go index 25ee4f644e..f6296ffb0b 100644 --- a/server/storage/interface.go +++ b/server/storage/interface.go @@ -1,10 +1,12 @@ package storage +import "github.com/endophage/gotuf/data" + // MetaStore holds the methods that are used for a Metadata Store type MetaStore interface { UpdateCurrent(gun, role string, version int, data []byte) error GetCurrent(gun, tufRole string) (data []byte, err error) Delete(gun string) error - GetTimestampKey(gun string) (cipher string, public []byte, err error) - SetTimestampKey(gun, cipher string, public []byte) error + GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error) + SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error } diff --git a/server/storage/memory.go b/server/storage/memory.go index a4d4bfe441..99ac4ed5bd 100644 --- a/server/storage/memory.go +++ b/server/storage/memory.go @@ -4,11 +4,13 @@ import ( "fmt" "strings" "sync" + + "github.com/endophage/gotuf/data" ) type key struct { - cipher string - public []byte + algorithm data.KeyAlgorithm + public []byte } type ver struct { @@ -73,7 +75,7 @@ func (st *MemStorage) Delete(gun string) error { } // GetTimestampKey returns the public key material of the timestamp key of a given gun -func (st *MemStorage) GetTimestampKey(gun string) (cipher string, public []byte, err error) { +func (st *MemStorage) GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error) { // no need for lock. It's ok to return nil if an update // wasn't observed k, ok := st.tsKeys[gun] @@ -81,12 +83,12 @@ func (st *MemStorage) GetTimestampKey(gun string) (cipher string, public []byte, return "", nil, &ErrNoKey{gun: gun} } - return k.cipher, k.public, nil + return k.algorithm, k.public, nil } // SetTimestampKey sets a Timestamp key under a gun -func (st *MemStorage) SetTimestampKey(gun, cipher string, public []byte) error { - k := &key{cipher: cipher, public: public} +func (st *MemStorage) SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error { + k := &key{algorithm: algorithm, public: public} st.lock.Lock() defer st.lock.Unlock() if _, ok := st.tsKeys[gun]; ok { diff --git a/server/storage/memory_test.go b/server/storage/memory_test.go index c6a7f00d53..aba7722b80 100644 --- a/server/storage/memory_test.go +++ b/server/storage/memory_test.go @@ -51,7 +51,7 @@ func TestGetTimestampKey(t *testing.T) { c, k, err := s.GetTimestampKey("gun") assert.Nil(t, err, "Expected error to be nil") - assert.Equal(t, data.RSAKey, c, "Expected cipher rsa, received %s", c) + assert.Equal(t, data.RSAKey, c, "Expected algorithm rsa, received %s", c) assert.Equal(t, []byte("test"), k, "Key data was wrong") } @@ -63,7 +63,7 @@ func TestSetTimestampKey(t *testing.T) { assert.IsType(t, &ErrTimestampKeyExists{}, err, "Expected err to be ErrTimestampKeyExists") k := s.tsKeys["gun"] - assert.Equal(t, data.RSAKey, k.cipher, "Expected cipher to be rsa, received %s", k.cipher) + assert.Equal(t, data.RSAKey, k.algorithm, "Expected algorithm to be rsa, received %s", k.algorithm) assert.Equal(t, []byte("test"), k.public, "Public key did not match expected") } diff --git a/server/timestamp/timestamp.go b/server/timestamp/timestamp.go index e558c7a7b9..88814f369f 100644 --- a/server/timestamp/timestamp.go +++ b/server/timestamp/timestamp.go @@ -17,28 +17,28 @@ import ( // found. It attempts to handle the race condition that may occur if 2 servers try to // create the key at the same time by simply querying the store a second time if it // receives a conflict when writing. -func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService) (*data.TUFKey, error) { - cipher, public, err := store.GetTimestampKey(gun) +func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService, fallBackAlgorithm data.KeyAlgorithm) (*data.TUFKey, error) { + keyAlgorithm, public, err := store.GetTimestampKey(gun) if err == nil { - return data.NewTUFKey(cipher, public, nil), nil + return data.NewTUFKey(keyAlgorithm, public, nil), nil } if _, ok := err.(*storage.ErrNoKey); ok { - key, err := crypto.Create("timestamp") + key, err := crypto.Create("timestamp", fallBackAlgorithm) if err != nil { return nil, err } - err = store.SetTimestampKey(gun, key.Cipher(), key.Public()) + err = store.SetTimestampKey(gun, key.Algorithm(), key.Public()) if err == nil { return &key.TUFKey, nil } if _, ok := err.(*storage.ErrTimestampKeyExists); ok { - cipher, public, err = store.GetTimestampKey(gun) + keyAlgorithm, public, err = store.GetTimestampKey(gun) if err != nil { return nil, err } - return data.NewTUFKey(cipher, public, nil), nil + return data.NewTUFKey(keyAlgorithm, public, nil), nil } return nil, err } @@ -96,13 +96,13 @@ func timestampExpired(ts *data.SignedTimestamp) bool { // version number one higher than prev. The store is used to lookup the current // snapshot, this function does not save the newly generated timestamp. func createTimestamp(gun string, prev *data.SignedTimestamp, store storage.MetaStore, signer *signed.Signer) (*data.Signed, int, error) { - cipher, public, err := store.GetTimestampKey(gun) + algorithm, public, err := store.GetTimestampKey(gun) if err != nil { // owner of gun must have generated a timestamp key otherwise // we won't proceed with generating everything. return nil, 0, err } - key := data.NewPublicKey(cipher, public) + key := data.NewPublicKey(algorithm, public) snapshot, err := store.GetCurrent(gun, "snapshot") if err != nil { return nil, 0, err diff --git a/server/timestamp/timestamp_test.go b/server/timestamp/timestamp_test.go index e700439c6f..3c7ed5d5c3 100644 --- a/server/timestamp/timestamp_test.go +++ b/server/timestamp/timestamp_test.go @@ -32,11 +32,11 @@ func TestTimestampExpired(t *testing.T) { func TestGetTimestampKey(t *testing.T) { store := storage.NewMemStorage() crypto := signed.NewEd25519() - k, err := GetOrCreateTimestampKey("gun", store, crypto) + k, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key) assert.Nil(t, err, "Expected nil error") assert.NotNil(t, k, "Key should not be nil") - k2, err := GetOrCreateTimestampKey("gun", store, crypto) + k2, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key) assert.Nil(t, err, "Expected nil error") @@ -55,7 +55,7 @@ func TestGetTimestamp(t *testing.T) { store.UpdateCurrent("gun", "snapshot", 0, snapJSON) // create a key to be used by GetTimestamp - _, err := GetOrCreateTimestampKey("gun", store, crypto) + _, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key) assert.Nil(t, err, "GetTimestampKey errored") _, err = GetOrCreateTimestamp("gun", store, signer) diff --git a/signer/rufus_trust.go b/signer/rufus_trust.go index adf90b9bba..3342a8b2bd 100644 --- a/signer/rufus_trust.go +++ b/signer/rufus_trust.go @@ -54,21 +54,22 @@ func (trust *RufusSigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature } signatures = append(signatures, data.Signature{ KeyID: sig.KeyID.ID, - Method: sig.Algorithm.Algorithm, + Method: data.SigAlgorithm(sig.Algorithm.Algorithm), Signature: sig.Content, }) } return signatures, nil } +// TODO(diogo): Ignoring algorithm for now until rufus supports it // Create creates a remote key and returns the PublicKey associated with the remote private key -func (trust *RufusSigner) Create(role string) (*data.PublicKey, error) { +func (trust *RufusSigner) Create(role string, _ data.KeyAlgorithm) (*data.PublicKey, error) { publicKey, err := trust.kmClient.CreateKey(context.Background(), &pb.Void{}) if err != nil { return nil, err } //TODO(mccauley): Update API to return algorithm and/or take it as a param - public := data.NewPublicKey(publicKey.Algorithm.Algorithm, publicKey.PublicKey) + public := data.NewPublicKey(data.KeyAlgorithm(publicKey.Algorithm.Algorithm), publicKey.PublicKey) return public, nil } @@ -82,7 +83,7 @@ func (trust *RufusSigner) PublicKeys(keyIDs ...string) (map[string]*data.PublicK return nil, err } publicKeys[public.KeyID.ID] = - data.NewPublicKey(public.Algorithm.Algorithm, public.PublicKey) + data.NewPublicKey(data.KeyAlgorithm(public.Algorithm.Algorithm), public.PublicKey) } return publicKeys, nil } diff --git a/trustmanager/x509utils.go b/trustmanager/x509utils.go index 614ad8b87a..98c615673a 100644 --- a/trustmanager/x509utils.go +++ b/trustmanager/x509utils.go @@ -100,7 +100,7 @@ func fingerprintCert(cert *x509.Certificate) (CertID, error) { block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw} pemdata := pem.EncodeToMemory(&block) - keyType := "" + var keyType data.KeyAlgorithm switch cert.PublicKeyAlgorithm { case x509.RSA: keyType = data.RSAx509Key @@ -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, keyType string) (*data.PrivateKey, error) { +func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey, keyType data.KeyAlgorithm) (*data.PrivateKey, error) { // Get a DER-encoded representation of the PublicKey rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey) if err != nil { @@ -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, keyType string) (*data.PrivateKey, error) { +func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey, keyType data.KeyAlgorithm) (*data.PrivateKey, error) { // Get a DER-encoded representation of the PublicKey ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey) if err != nil { @@ -280,15 +280,15 @@ func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey, keyType string) (*data.Pr // KeyToPEM returns a PEM encoded key from a Private Key func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) { var pemType string - cipher := privKey.Cipher() + algorithm := privKey.Algorithm() - switch cipher { + switch algorithm { case data.RSAKey: pemType = "RSA PRIVATE KEY" case data.ECDSAKey: pemType = "EC PRIVATE KEY" default: - return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher) + return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm) } return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: privKey.Private()}), nil @@ -298,15 +298,15 @@ func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) { // and a passphrase func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) { var blockType string - cipher := key.Cipher() + algorithm := key.Algorithm() - switch cipher { + switch algorithm { case data.RSAKey: blockType = "RSA PRIVATE KEY" case data.ECDSAKey: blockType = "EC PRIVATE KEY" default: - return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher) + return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm) } password := []byte(passphrase)