Refactor crypto service

Signed-off-by: Diogo Monica <diogo@docker.com>
This commit is contained in:
Diogo Monica 2015-07-13 13:53:47 -07:00
parent 17af21f00c
commit 765a2cf661
13 changed files with 142 additions and 214 deletions

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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())
}

View File

@ -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,

View File

@ -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}

View File

@ -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
}

View File

@ -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 {

View File

@ -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")
}

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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)