Merge pull request #72 from docker/passphrase_callbacks

Update keyfilestore to use passwordRetriever
This commit is contained in:
Diogo Mónica 2015-07-20 11:45:45 -07:00
commit 54b312754f
22 changed files with 547 additions and 463 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ bin
cross
.cover
*.swp
.idea
*.iml

View File

@ -49,7 +49,7 @@ test:
pwd: $BASE_STABLE
# VET
- gvm use stable && go vet ./...:
- gvm use stable && test -z "$(go tool vet -printf=false . 2>&1 | grep -v Godeps/_workspace/src/ | tee /dev/stderr)":
pwd: $BASE_STABLE
# LINT

View File

@ -29,8 +29,6 @@ const maxSize = 5 << 20
// notary repository
type ErrRepoNotInitialized struct{}
type passwordRetriever func() (string, error)
// ErrRepoNotInitialized is returned when trying to can publish on an uninitialized
// notary repository
func (err *ErrRepoNotInitialized) Error() string {
@ -85,13 +83,15 @@ func NewTarget(targetName string, targetPath string) (*Target, error) {
// NewNotaryRepository is a helper method that returns a new notary repository.
// It takes the base directory under where all the trust files will be stored
// (usually ~/.docker/trust/).
func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*NotaryRepository, error) {
keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir)
func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper,
passphraseRetriever trustmanager.PassphraseRetriever) (*NotaryRepository, error) {
keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir, passphraseRetriever)
if err != nil {
return nil, err
}
cryptoService := cryptoservice.NewCryptoService(gun, keyStoreManager.NonRootKeyStore(), "")
cryptoService := cryptoservice.NewCryptoService(gun, keyStoreManager.NonRootKeyStore())
nRepo := &NotaryRepository{
gun: gun,
@ -138,7 +138,7 @@ func (r *NotaryRepository) Initialize(uCryptoService *cryptoservice.UnlockedCryp
// 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(), uCryptoService.ID())
err = r.KeyStoreManager.RootKeyStore().Link(uCryptoService.ID(), rootKey.ID())
err = r.KeyStoreManager.RootKeyStore().Link(uCryptoService.ID()+"_root", rootKey.ID()+"_root")
if err != nil {
return err
}
@ -300,7 +300,7 @@ func (r *NotaryRepository) GetTargetByName(name string) (*Target, error) {
// Publish pushes the local changes in signed material to the remote notary-server
// Conceptually it performs an operation similar to a `git rebase`
func (r *NotaryRepository) Publish(getPass passwordRetriever) error {
func (r *NotaryRepository) Publish() error {
var updateRoot bool
var root *data.Signed
// attempt to initialize the repo from the remote store
@ -356,12 +356,11 @@ func (r *NotaryRepository) Publish(getPass passwordRetriever) error {
// check if our root file is nearing expiry. Resign if it is.
if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty {
passphrase, err := getPass()
if err != nil {
return err
}
rootKeyID := r.tufRepo.Root.Signed.Roles["root"].KeyIDs[0]
rootCryptoService, err := r.KeyStoreManager.GetRootCryptoService(rootKeyID, passphrase)
rootCryptoService, err := r.KeyStoreManager.GetRootCryptoService(rootKeyID)
if err != nil {
return err
}

View File

@ -21,6 +21,8 @@ type SignedRSARootTemplate struct {
RootPem string
}
var passphraseRetriever = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil }
const validPEMEncodedRSARoot = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZLekNDQXhXZ0F3SUJBZ0lRUnlwOVFxY0pmZDNheXFkaml6OHhJREFMQmdrcWhraUc5dzBCQVFzd09ERWEKTUJnR0ExVUVDaE1SWkc5amEyVnlMbU52YlM5dWIzUmhjbmt4R2pBWUJnTlZCQU1URVdSdlkydGxjaTVqYjIwdgpibTkwWVhKNU1CNFhEVEUxTURjeE56QTJNelF5TTFvWERURTNNRGN4TmpBMk16UXlNMW93T0RFYU1CZ0dBMVVFCkNoTVJaRzlqYTJWeUxtTnZiUzl1YjNSaGNua3hHakFZQmdOVkJBTVRFV1J2WTJ0bGNpNWpiMjB2Ym05MFlYSjUKTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFvUWZmcnpzWW5zSDh2R2Y0Smg1NQpDajV3cmpVR3pEL3NIa2FGSHB0ako2VG9KR0p2NXlNQVB4enlJbnU1c0lvR0xKYXBuWVZCb0FVMFlnSTlxbEFjCllBNlN4YVN3Z202cnB2bW5sOFFuMHFjNmdlcjNpbnBHYVVKeWxXSHVQd1drdmNpbVFBcUhaeDJkUXRMN2c2a3AKcm1LZVRXcFdvV0x3M0pvQVVaVVZoWk1kNmEyMlpML0R2QXcrSHJvZ2J6NFhleWFoRmI5SUg0MDJ6UHhONnZnYQpKRUZURjBKaTFqdE5nME1vNHBiOVNIc01zaXcrTFpLN1NmZkhWS1B4dmQyMW0vYmlObXdzZ0V4QTNVOE9PRzhwCnV5Z2ZhY3lzNWM4K1pyWCtaRkcvY3Z3S3owazYvUWZKVTQwczZNaFh3NUMyV3R0ZFZtc0c5LzdyR0ZZakhvSUoKd2VEeXhnV2s3dnhLelJKSS91bjdjYWdESWFRc0tySlFjQ0hJR0ZSbHBJUjVUd1g3dmwzUjdjUm5jckRSTVZ2YwpWU0VHMmVzeGJ3N2p0eklwL3lwblZSeGNPbnk3SXlweWpLcVZlcVo2SGd4WnRUQlZyRjFPL2FIbzJrdmx3eVJTCkF1czRrdmg2ejMranpUbTlFemZYaVBRelk5QkVrNWdPTHhoVzlyYzZVaGxTK3BlNWxrYU4vSHlxeS9sUHVxODkKZk1yMnJyN2xmNVdGZEZuemU2V05ZTUFhVzdkTkE0TkUwZHlENTM0MjhaTFh4TlZQTDRXVTY2R2FjNmx5blE4bApyNXRQc1lJRlh6aDZGVmFSS0dRVXRXMWh6OWVjTzZZMjdSaDJKc3lpSXhnVXFrMm9veEU2OXVONDJ0K2R0cUtDCjFzOEcvN1Z0WThHREFMRkxZVG56THZzQ0F3RUFBYU0xTURNd0RnWURWUjBQQVFIL0JBUURBZ0NnTUJNR0ExVWQKSlFRTU1Bb0dDQ3NHQVFVRkJ3TURNQXdHQTFVZEV3RUIvd1FDTUFBd0N3WUpLb1pJaHZjTkFRRUxBNElDQVFCTQpPbGwzRy9YQno4aWRpTmROSkRXVWgrNXczb2ptd2FuclRCZENkcUVrMVdlbmFSNkR0Y2ZsSng2WjNmL213VjRvCmIxc2tPQVgxeVg1UkNhaEpIVU14TWljei9RMzhwT1ZlbEdQclduYzNUSkIrVktqR3lIWGxRRFZrWkZiKzQrZWYKd3RqN0huZ1hoSEZGRFNnam0zRWRNbmR2Z0RRN1NRYjRza09uQ05TOWl5WDdlWHhoRkJDWm1aTCtIQUxLQmoyQgp5aFY0SWNCRHFtcDUwNHQxNHJ4OS9KdnR5MGRHN2ZZN0k1MWdFUXBtNFMwMkpNTDV4dlRtMXhmYm9XSWhaT0RJCnN3RUFPK2VrQm9GSGJTMVE5S01QaklBdzNUckNISDh4OFhacTV6c1l0QUMxeVpIZENLYTI2YVdkeTU2QTllSGoKTzFWeHp3bWJOeVhSZW5WdUJZUCswd3IzSFZLRkc0Sko0WlpwTlp6UVcvcHFFUGdoQ1RKSXZJdWVLNjUyQnlVYwovL3N2K25YZDVmMTlMZUVTOXBmMGwyNTNORGFGWlBiNmFlZ0tmcXVXaDhxbFFCbVVRMkd6YVRMYnRtTmQyOE02Clc3aUw3dGtLWmUxWm5CejlSS2d0UHJEampXR1pJbmpqY09VOEV0VDRTTHE3a0NWRG1QczVNRDh2YUFtOTZKc0UKam1MQzNVdS80azdIaURZWDBpMG1PV2tGalpRTWRWYXRjSUY1RlBTcHB3c1NiVzhRaWRuWHQ1NFV0d3RGREVQegpscGpzN3liZVFFNzFKWGNNWm5WSUs0YmpSWHNFRlBJOThScElsRWRlZGJTVWRZQW5jTE5KUlQ3SFpCTVBHU3daCjBQTkp1Z2xubHIzc3JWemRXMWR6MnhRamR2THd4eTZtTlVGNnJiUUJXQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K`
const validCAPEMEncodeRSARoot = `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlHTXpDQ0JCdWdBd0lCQWdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURCZk1Rc3dDUVlEVlFRR0V3SlZVekVMDQpNQWtHQTFVRUNBd0NRMEV4RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEekFOQmdOVkJBb01Ca1J2DQpZMnRsY2pFYU1CZ0dBMVVFQXd3UlRtOTBZWEo1SUZSbGMzUnBibWNnUTBFd0hoY05NVFV3TnpFMk1EUXlOVEF6DQpXaGNOTWpVd056RXpNRFF5TlRBeldqQmZNUm93R0FZRFZRUUREQkZPYjNSaGNua2dWR1Z6ZEdsdVp5QkRRVEVMDQpNQWtHQTFVRUJoTUNWVk14RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEekFOQmdOVkJBb01Ca1J2DQpZMnRsY2pFTE1Ba0dBMVVFQ0F3Q1EwRXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDRHdBd2dnSUtBb0lDDQpBUUN3VlZENHBLN3o3cFhQcEpiYVoxSGc1ZVJYSWNhWXRiRlBDbk4waXF5OUhzVkVHbkVuNUJQTlNFc3VQK20wDQo1TjBxVlY3REdiMVNqaWxvTFhEMXFERHZoWFdrK2dpUzlwcHFQSFBMVlBCNGJ2enNxd0RZcnRwYnFrWXZPMFlLDQowU0wza3hQWFVGZGxrRmZndTB4amxjem0yUGhXRzNKZDhhQXRzcEwvTCtWZlBBMTNKVWFXeFNMcHVpMUluOHJoDQpnQXlRVEs2UTRPZjZHYkpZVG5BSGI1OVVvTFhTekI1QWZxaVVxNkw3bkVZWUtvUGZsUGJSQUlXTC9VQm0wYytIDQpvY21zNzA2UFlwbVBTMlJRdjNpT0dtbm45aEVWcDNQNmpxN1dBZXZiQTRhWUd4NUVzYlZ0WUFCcUpCYkZXQXV3DQp3VEdSWW16bjBNajBlVE1nZTl6dFlCMi8yc3hkVGU2dWhtRmdwVVhuZ0RxSkk1TzlOM3pQZnZsRUltQ2t5M0hNDQpqSm9MN2c1c21xWDlvMVArRVNMaDBWWnpoaDdJRFB6UVRYcGNQSVMvNnowbDIyUUdrSy8xTjFQYUFEYVVIZExMDQp2U2F2M3kyQmFFbVB2ZjJma1pqOHlQNWVZZ2k3Q3c1T05oSExEWUhGY2w5Wm0veXdtZHhISkVUejluZmdYbnNXDQpITnhEcXJrQ1ZPNDZyL3U2clNyVXQ2aHIzb2RkSkc4czhKbzA2ZWFydzZYVTNNek0rM2dpd2tLMFNTTTN1UlBxDQo0QXNjUjFUditFMzFBdU9BbWpxWVFvVDI5Yk1JeG9TemVsamovWW5lZHdqVzQ1cFd5YzNKb0hhaWJEd3ZXOVVvDQpHU1pCVnk0aHJNL0ZhN1hDV3YxV2ZITlcxZ0R3YUxZd0RubDVqRm1SQnZjZnVRSURBUUFCbzRINU1JSDJNSUdSDQpCZ05WSFNNRWdZa3dnWWFBRkhVTTFVM0U0V3lMMW52RmQrZFBZOGY0TzJoWm9XT2tZVEJmTVFzd0NRWURWUVFHDQpFd0pWVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhGakFVQmdOVkJBY01EVk5oYmlCR2NtRnVZMmx6WTI4eER6QU5CZ05WDQpCQW9NQmtSdlkydGxjakVhTUJnR0ExVUVBd3dSVG05MFlYSjVJRlJsYzNScGJtY2dRMEdDQ1FEQ2VETGJlbUlUDQpTekFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9BZ0VBTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBT0JnTlZIUThCQWY4RUJBTUNBVVl3SFFZRFZSME9CQllFRkhlNDhoY0JjQXAwYlVWbFR4WGVSQTRvDQpFMTZwTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFBV1V0QVBkVUZwd1JxK04xU3pHVWVqU2lrZU1HeVBac2NaDQpKQlVDbWhab0Z1ZmdYR2JMTzVPcGNSTGFWM1hkYTB0LzVQdGRHTVNFemN6ZW9aSFdrbkR0dys3OU9CaXR0UFBqDQpTaDFvRkR1UG8zNVI3ZVA2MjRsVUNjaC9JblpDcGhUYUx4OW9ETEdjYUszYWlsUTl3akJkS2RsQmw4S05LSVpwDQphMTNhUDVyblNtMkp2YSt0WHkveWkzQlNkczNkR0Q4SVRLWnlJLzZBRkh4R3ZPYnJESUJwbzRGRi96Y1dYVkRqDQpwYU9teHBsUnRNNEhpdG0rc1hHdmZxSmU0eDVEdU9YT25QclQzZEh2UlQ2dlNaVW9Lb2J4TXFtUlRPY3JPSVBhDQpFZU1wT29ic2hPUnVSbnRNRFl2dmdPM0Q2cDZpY2lEVzJWcDlONnJkTWRmT1dFUU44SlZXdkI3SXhSSGs5cUtKDQp2WU9XVmJjekF0MHFwTXZYRjNQWExqWmJVTTBrbk9kVUtJRWJxUDRZVWJnZHp4NlJ0Z2lpWTkzMEFqNnRBdGNlDQowZnBnTmx2ak1ScFNCdVdUbEFmTk5qRy9ZaG5kTXo5dUk2OFRNZkZwUjNQY2dWSXYzMGtydy85VnpvTGkyRHBlDQpvdzZEckdPNm9pK0RoTjc4UDRqWS9POVVjelpLMnJvWkwxT2k1UDBSSXhmMjNVWkM3eDFEbGNOM25CcjRzWVN2DQpyQng0Y0ZUTU5wd1UrbnpzSWk0ZGpjRkRLbUpkRU95ak1ua1AydjBMd2U3eXZLMDhwWmRFdSswemJycTE3a3VlDQpYcFhMYzdLNjhRQjE1eXh6R3lsVTVyUnd6bUMvWXNBVnlFNGVvR3U4UHhXeHJFUnZIYnk0QjhZUDB2QWZPcmFMDQpsS21YbEs0ZFRnPT0NCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==`
@ -49,13 +51,13 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) {
ts, mux := createTestServer(t)
defer ts.Close()
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
assert.NoError(t, err, "error creating repository: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String(), "passphrase")
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, "passphrase")
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -71,7 +73,7 @@ func validateRootSuccessfully(t *testing.T, rootType data.KeyAlgorithm) {
var tempKey data.TUFKey
json.Unmarshal([]byte(timestampECDSAKeyJSON), &tempKey)
repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), &tempKey)
repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), "root", &tempKey)
// Because ListTargets will clear this
savedTUFRepo := repo.tufRepo
@ -200,7 +202,7 @@ func TestValidateRootWithInvalidData(t *testing.T) {
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
// Create a FileStoreManager
keyStoreManager, err := keystoremanager.NewKeyStoreManager(tempBaseDir)
keyStoreManager, err := keystoremanager.NewKeyStoreManager(tempBaseDir, passphraseRetriever)
assert.NoError(t, err)
// Execute our template

View File

@ -63,13 +63,13 @@ func testInitRepo(t *testing.T, rootType data.KeyAlgorithm) {
ts, _ := createTestServer(t)
defer ts.Close()
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String(), "passphrase")
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, "passphrase")
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -95,14 +95,15 @@ func testInitRepo(t *testing.T, rootType data.KeyAlgorithm) {
// in the private key store.
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(true)
for _, privKeyName := range privKeyList {
_, err := os.Stat(privKeyName)
privKeyFileName := filepath.Join(repo.KeyStoreManager.NonRootKeyStore().BaseDir(), privKeyName)
_, err := os.Stat(privKeyFileName)
assert.NoError(t, err, "missing private key: %s", privKeyName)
}
// Look for keys in root_keys
// There should be a file named after the key ID of the root key we
// passed in.
rootKeyFilename := rootCryptoService.ID() + ".key"
rootKeyFilename := rootCryptoService.ID() + "_root.key"
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
assert.NoError(t, err, "missing root key")
@ -114,7 +115,7 @@ func testInitRepo(t *testing.T, rootType data.KeyAlgorithm) {
certID, err := trustmanager.FingerprintCert(certificates[0])
assert.NoError(t, err, "unable to fingerprint the certificate")
actualDest, err := os.Readlink(filepath.Join(tempBaseDir, "private", "root_keys", certID+".key"))
actualDest, err := os.Readlink(filepath.Join(tempBaseDir, "private", "root_keys", certID+"_root"+".key"))
assert.NoError(t, err, "missing symlink to root key")
assert.Equal(t, rootKeyFilename, actualDest, "symlink to root key has wrong destination")
@ -206,13 +207,13 @@ func testAddListTarget(t *testing.T, rootType data.KeyAlgorithm) {
ts, mux := createTestServer(t)
defer ts.Close()
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
assert.NoError(t, err, "error creating repository: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String(), "passphrase")
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, "passphrase")
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retreiving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -311,7 +312,7 @@ func testAddListTarget(t *testing.T, rootType data.KeyAlgorithm) {
var tempKey data.TUFKey
json.Unmarshal([]byte(timestampECDSAKeyJSON), &tempKey)
repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), &tempKey)
repo.KeyStoreManager.NonRootKeyStore().AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), "nonroot", &tempKey)
// Because ListTargets will clear this
savedTUFRepo := repo.tufRepo
@ -395,13 +396,13 @@ func testValidateRootKey(t *testing.T, rootType data.KeyAlgorithm) {
ts, _ := createTestServer(t)
defer ts.Close()
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
assert.NoError(t, err, "error creating repository: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String(), "passphrase")
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, "passphrase")
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retreiving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -459,7 +460,8 @@ func testPublish(t *testing.T, rootType data.KeyAlgorithm) {
// Set up server
ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage())
hand := utils.RootHandlerFactory(nil, ctx, cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(), ""))
hand := utils.RootHandlerFactory(nil, ctx,
cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(passphraseRetriever)))
r := mux.NewRouter()
r.Methods("POST").Path("/v2/{imageName:.*}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull"))
@ -471,13 +473,13 @@ func testPublish(t *testing.T, rootType data.KeyAlgorithm) {
ts := httptest.NewServer(r)
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
assert.NoError(t, err, "error creating repository: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String(), "passphrase")
rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, "passphrase")
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retreiving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -561,9 +563,7 @@ func testPublish(t *testing.T, rootType data.KeyAlgorithm) {
changelistDir.Close()
// Now test Publish
err = repo.Publish(func() (string, error) {
return "passphrase", nil
})
err = repo.Publish()
assert.NoError(t, err)
changelistDir, err = os.Open(changelistDirPath)
@ -579,7 +579,7 @@ func testPublish(t *testing.T, rootType data.KeyAlgorithm) {
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
repo2, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo2, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever)
assert.NoError(t, err, "error creating repository: %s", err)
targets, err := repo2.ListTargets()

View File

@ -3,11 +3,10 @@
"addr": ":4443"
},
"trust_service": {
"type": "remote",
"hostname": "notarysigner",
"port": "7899",
"tls_ca_file": "./fixtures/root-ca.crt"
},
"type": "remote",
"hostname": "notarysigner",
"port": "7899",
"tls_ca_file": "./fixtures/root-ca.crt" },
"logging": {
"level": 5
},

View File

@ -41,6 +41,12 @@ func init() {
flag.BoolVar(&debug, "debug", false, "show the version and exit")
}
func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
//TODO(mccauley) Read from config once we have locked keys in notary-signer
return "", false, nil
}
func main() {
flag.Usage = usage
flag.Parse()
@ -83,8 +89,8 @@ func main() {
cryptoServices[data.RSAKey] = api.NewRSAHardwareCryptoService(ctx, session)
}
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
cryptoServices[data.ED25519Key] = cryptoService
cryptoServices[data.ECDSAKey] = cryptoService

View File

@ -86,11 +86,11 @@ func init() {
fatalf("could not create Certificate X509FileStore: %v", err)
}
privKeyStore, err = trustmanager.NewKeyFileStore(finalPrivDir)
privKeyStore, err = trustmanager.NewKeyFileStore(finalPrivDir,
func(string, string, bool, int) (string, bool, error) { return "", false, nil })
if err != nil {
fatalf("could not create KeyFileStore: %v", err)
}
}
func main() {

View File

@ -2,7 +2,6 @@ package main
import (
"bufio"
"bytes"
"crypto/sha256"
"crypto/tls"
"errors"
@ -14,13 +13,21 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/term"
notaryclient "github.com/docker/notary/client"
"github.com/docker/notary/trustmanager"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"strings"
)
// FIXME: This should not be hardcoded
const hardcodedBaseURL = "https://notary-server:4443"
var retriever trustmanager.PassphraseRetriever
func init() {
retriever = getNotaryPassphraseRetriever()
}
var remoteTrustServer string
var cmdTufList = &cobra.Command{
@ -82,7 +89,8 @@ func tufAdd(cmd *cobra.Command, args []string) {
targetName := args[1]
targetPath := args[2]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, getInsecureTransport())
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), retriever)
if err != nil {
fatalf(err.Error())
}
@ -106,35 +114,26 @@ func tufInit(cmd *cobra.Command, args []string) {
gun := args[0]
nRepo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, getInsecureTransport())
nRepo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), retriever)
if err != nil {
fatalf(err.Error())
}
keysList := nRepo.KeyStoreManager.RootKeyStore().ListKeys()
var passphrase string
var rootKeyID string
if len(keysList) < 1 {
fmt.Println("No root keys found. Generating a new root key...")
passphrase, err = passphraseRetriever()
if err != nil {
fatalf(err.Error())
}
rootKeyID, err = nRepo.KeyStoreManager.GenRootKey("ECDSA", passphrase)
rootKeyID, err = nRepo.KeyStoreManager.GenRootKey("ECDSA")
if err != nil {
fatalf(err.Error())
}
} else {
rootKeyID = keysList[0]
fmt.Println("Root key found.")
fmt.Printf("Enter passphrase for: %s (%d)\n", rootKeyID, len(rootKeyID))
passphrase, err = passphraseRetriever()
if err != nil {
fatalf(err.Error())
}
}
rootCryptoService, err := nRepo.KeyStoreManager.GetRootCryptoService(rootKeyID, passphrase)
rootCryptoService, err := nRepo.KeyStoreManager.GetRootCryptoService(rootKeyID)
if err != nil {
fatalf(err.Error())
}
@ -151,8 +150,8 @@ func tufList(cmd *cobra.Command, args []string) {
fatalf("must specify a GUN")
}
gun := args[0]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, getInsecureTransport())
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), retriever)
if err != nil {
fatalf(err.Error())
}
@ -177,7 +176,8 @@ func tufLookup(cmd *cobra.Command, args []string) {
gun := args[0]
targetName := args[1]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, getInsecureTransport())
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), retriever)
if err != nil {
fatalf(err.Error())
}
@ -201,12 +201,13 @@ func tufPublish(cmd *cobra.Command, args []string) {
fmt.Println("Pushing changes to ", gun, ".")
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, getInsecureTransport())
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), retriever)
if err != nil {
fatalf(err.Error())
}
err = repo.Publish(passphraseRetriever)
err = repo.Publish()
if err != nil {
fatalf(err.Error())
}
@ -246,7 +247,8 @@ func verify(cmd *cobra.Command, args []string) {
//TODO (diogo): This code is copy/pasted from lookup.
gun := args[0]
targetName := args[1]
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL, getInsecureTransport())
repo, err := notaryclient.NewNotaryRepository(viper.GetString("baseTrustDir"), gun, hardcodedBaseURL,
getInsecureTransport(), retriever)
if err != nil {
fatalf(err.Error())
}
@ -270,58 +272,91 @@ func verify(cmd *cobra.Command, args []string) {
return
}
func passphraseRetriever() (string, error) {
fmt.Println("Please provide a passphrase for this root key: ")
var passphrase string
_, err := fmt.Scanln(&passphrase)
if err != nil {
return "", err
}
if len(passphrase) < 8 {
fmt.Println("Please use a password manager to generate and store a good random passphrase.")
return "", errors.New("Passphrase too short")
}
return passphrase, nil
}
func getNotaryPassphraseRetriever() trustmanager.PassphraseRetriever {
userEnteredTargetsSnapshotsPass := false
targetsSnapshotsPass := ""
userEnteredRootsPass := false
rootsPass := ""
func getPassphrase(confirm bool) ([]byte, error) {
if pass := os.Getenv("NOTARY_ROOT_PASSPHRASE"); pass != "" {
return []byte(pass), nil
}
return func(keyID string, alias string, createNew bool, numAttempts int) (string, bool, error) {
state, err := term.SaveState(0)
if err != nil {
return nil, err
}
term.DisableEcho(0, state)
defer term.RestoreTerminal(0, state)
// First, check if we have a password cached for this alias.
if numAttempts == 0 {
if userEnteredTargetsSnapshotsPass && (alias == "snapshot" || alias == "targets") {
return targetsSnapshotsPass, false, nil
}
if userEnteredRootsPass && (alias == "root") {
return rootsPass, false, nil
}
}
stdin := bufio.NewReader(os.Stdin)
if numAttempts > 3 && !createNew {
return "", true, errors.New("Too many attempts")
}
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]
state, err := term.SaveState(0)
if err != nil {
return "", false, err
}
term.DisableEcho(0, state)
defer term.RestoreTerminal(0, state)
if !confirm {
return passphrase, nil
}
stdin := bufio.NewReader(os.Stdin)
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 createNew {
fmt.Printf("Enter passphrase for new %s key with id %s: ", alias, keyID)
} else {
fmt.Printf("Enter key passphrase for %s key with id %s: ", alias, keyID)
}
if !bytes.Equal(passphrase, confirmation) {
return nil, errors.New("The entered passphrases do not match")
passphrase, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return "", false, err
}
retPass := strings.TrimSpace(string(passphrase))
if !createNew {
if alias == "snapshot" || alias == "targets" {
userEnteredTargetsSnapshotsPass = true
targetsSnapshotsPass = retPass
}
if alias == "root" {
userEnteredRootsPass = true
rootsPass = retPass
}
return retPass, false, nil
}
if len(retPass) < 8 {
fmt.Println("Please use a password manager to generate and store a good random passphrase.")
return "", false, errors.New("Passphrase too short")
}
fmt.Printf("Repeat passphrase for new %s key with id %s: ", alias, keyID)
confirmation, err := stdin.ReadBytes('\n')
fmt.Println()
if err != nil {
return "", false, err
}
confirmationStr := strings.TrimSpace(string(confirmation))
if retPass != confirmationStr {
return "", false, errors.New("The entered passphrases do not match")
}
if alias == "snapshot" || alias == "targets" {
userEnteredTargetsSnapshotsPass = true
targetsSnapshotsPass = retPass
}
if alias == "root" {
userEnteredRootsPass = true
rootsPass = retPass
}
return retPass, false, nil
}
return passphrase, nil
}
func getInsecureTransport() *http.Transport {

View File

@ -23,14 +23,13 @@ const (
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
// operate on
type CryptoService struct {
gun string
passphrase string
keyStore trustmanager.KeyStore
gun string
keyStore trustmanager.KeyStore
}
// NewCryptoService returns an instance of CryptoService
func NewCryptoService(gun string, keyStore trustmanager.KeyStore, passphrase string) *CryptoService {
return &CryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}
func NewCryptoService(gun string, keyStore trustmanager.KeyStore) *CryptoService {
return &CryptoService{gun: gun, keyStore: keyStore}
}
// Create is used to generate keys for targets, snapshots and timestamps
@ -59,8 +58,8 @@ func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (data
}
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)
// Store the private key into our keystore with the name being: /GUN/ID.key with an alias of role
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), role, privKey)
if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
}
@ -78,7 +77,7 @@ func (ccs *CryptoService) GetKey(keyID string) data.PublicKey {
// RemoveKey deletes a key by ID
func (ccs *CryptoService) RemoveKey(keyID string) error {
return ccs.keyStore.Remove(keyID)
return ccs.keyStore.RemoveKey(keyID)
}
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
@ -93,14 +92,9 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
var privKey data.PrivateKey
var err error
// Read PrivateKey from file and decrypt it if there is a passphrase.
if ccs.passphrase != "" {
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
} else {
privKey, err = ccs.keyStore.GetKey(keyName)
}
privKey, err = ccs.keyStore.GetKey(keyName)
if err != nil {
// Note that GetDecryptedKey always fails on InitRepo.
// Note that GetKey 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.

View File

@ -17,11 +17,13 @@ func TestCryptoService(t *testing.T) {
}
}
var passphraseRetriever = func(string, string, bool, int) (string, bool, error) { return "", false, nil }
func testCryptoService(t *testing.T, keyAlgo data.KeyAlgorithm, verifier signed.Verifier) {
content := []byte("this is a secret")
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := NewCryptoService("", keyStore)
// Test Create
tufKey, err := cryptoService.Create("", keyAlgo)

View File

@ -13,13 +13,12 @@ func TestUnlockedSigner(t *testing.T) {
privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
assert.NoError(t, err, "could not generate key")
keyStore := trustmanager.NewKeyMemoryStore()
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
passphrase := "passphrase"
err = keyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase)
err = keyStore.AddKey(privKey.ID(), "root", privKey)
assert.NoError(t, err, "could not add key to store")
cryptoService := NewCryptoService("", keyStore, passphrase)
cryptoService := NewCryptoService("", keyStore)
uCryptoService := NewUnlockedCryptoService(privKey, cryptoService)
// Check ID method

View File

@ -13,7 +13,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/notary/trustmanager"
"github.com/endophage/gotuf/data"
)
var (
@ -25,10 +24,6 @@ var (
// unencrypted
ErrRootKeyNotEncrypted = errors.New("only encrypted root keys may be imported")
// ErrNonRootKeyEncrypted is returned if a non-root key is found to
// be encrypted while exporting
ErrNonRootKeyEncrypted = errors.New("found encrypted non-root key")
// ErrNoKeysFoundForGUN is returned if no keys are found for the
// specified GUN during export
ErrNoKeysFoundForGUN = errors.New("no keys found for specified GUN")
@ -37,7 +32,7 @@ var (
// ExportRootKey exports the specified root key to an io.Writer in PEM format.
// The key's existing encryption is preserved.
func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error {
pemBytes, err := km.rootKeyStore.Get(keyID)
pemBytes, err := km.rootKeyStore.Get(keyID + "_root")
if err != nil {
return err
}
@ -75,43 +70,27 @@ func (km *KeyStoreManager) ImportRootKey(source io.Reader, keyID string) error {
return err
}
if err = km.rootKeyStore.Add(keyID, pemBytes); err != nil {
if err = km.rootKeyStore.Add(keyID+"_root", pemBytes); err != nil {
return err
}
return err
}
func moveKeysWithNewPassphrase(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, outputPassphrase string) error {
func moveKeys(oldKeyStore, newKeyStore *trustmanager.KeyFileStore) error {
// List all files but no symlinks
for _, f := range oldKeyStore.ListFiles(false) {
fullKeyPath := strings.TrimSpace(strings.TrimSuffix(f, filepath.Ext(f)))
relKeyPath := strings.TrimPrefix(fullKeyPath, oldKeyStore.BaseDir())
relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator))
pemBytes, err := oldKeyStore.Get(relKeyPath)
for _, f := range oldKeyStore.ListKeys() {
pemBytes, err := oldKeyStore.GetKey(f)
if err != nil {
return err
}
block, _ := pem.Decode(pemBytes)
if block == nil {
return ErrNoValidPrivateKey
alias, err := oldKeyStore.GetKeyAlias(f)
if err != nil {
return err
}
if !x509.IsEncryptedPEMBlock(block) {
// Key is not encrypted. Parse it, and add it
// to the temporary store as an encrypted key.
privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
if err != nil {
return err
}
err = newKeyStore.AddEncryptedKey(relKeyPath, privKey, outputPassphrase)
} else {
// Encrypted key - pass it through without
// decrypting
err = newKeyStore.Add(relKeyPath, pemBytes)
}
err = newKeyStore.AddKey(f, alias, pemBytes)
if err != nil {
return err
@ -121,11 +100,10 @@ func moveKeysWithNewPassphrase(oldKeyStore, newKeyStore *trustmanager.KeyFileSto
return nil
}
func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileStore, tempBaseDir string) error {
func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileStore, subDir string) error {
// List all files but no symlinks
for _, fullKeyPath := range newKeyStore.ListFiles(false) {
relKeyPath := strings.TrimPrefix(fullKeyPath, tempBaseDir)
relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator))
for _, relKeyPath := range newKeyStore.ListFiles(false) {
fullKeyPath := filepath.Join(newKeyStore.BaseDir(), relKeyPath)
fi, err := os.Stat(fullKeyPath)
if err != nil {
@ -137,7 +115,7 @@ func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileSt
return err
}
infoHeader.Name = relKeyPath
infoHeader.Name = filepath.Join(subDir, relKeyPath)
zipFileEntryWriter, err := zipWriter.CreateHeader(infoHeader)
if err != nil {
return err
@ -156,40 +134,41 @@ func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileSt
}
// ExportAllKeys exports all keys to an io.Writer in zip format.
// outputPassphrase is the new passphrase to use to encrypt the existing keys.
// If blank, the keys will not be encrypted. Note that keys which are already
// encrypted are not re-encrypted. They will be included in the zip with their
// original encryption.
func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, outputPassphrase string) error {
// newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys.
func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever trustmanager.PassphraseRetriever) error {
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
defer os.RemoveAll(tempBaseDir)
privNonRootKeysSubdir := filepath.Join(privDir, nonRootKeysSubdir)
privRootKeysSubdir := filepath.Join(privDir, rootKeysSubdir)
// Create temporary keystores to use as a staging area
tempNonRootKeysPath := filepath.Join(tempBaseDir, privDir, nonRootKeysSubdir)
tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath)
tempNonRootKeysPath := filepath.Join(tempBaseDir, privNonRootKeysSubdir)
tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath, newPassphraseRetriever)
if err != nil {
return err
}
tempRootKeysPath := filepath.Join(tempBaseDir, privDir, rootKeysSubdir)
tempRootKeyStore, err := trustmanager.NewKeyFileStore(tempRootKeysPath)
tempRootKeysPath := filepath.Join(tempBaseDir, privRootKeysSubdir)
tempRootKeyStore, err := trustmanager.NewKeyFileStore(tempRootKeysPath, newPassphraseRetriever)
if err != nil {
return err
}
if err := moveKeysWithNewPassphrase(km.rootKeyStore, tempRootKeyStore, outputPassphrase); err != nil {
if err := moveKeys(km.rootKeyStore, tempRootKeyStore); err != nil {
return err
}
if err := moveKeysWithNewPassphrase(km.nonRootKeyStore, tempNonRootKeyStore, outputPassphrase); err != nil {
if err := moveKeys(km.nonRootKeyStore, tempNonRootKeyStore); err != nil {
return err
}
zipWriter := zip.NewWriter(dest)
if err := addKeysToArchive(zipWriter, tempRootKeyStore, tempBaseDir); err != nil {
if err := addKeysToArchive(zipWriter, tempRootKeyStore, privRootKeysSubdir); err != nil {
return err
}
if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, tempBaseDir); err != nil {
if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, privNonRootKeysSubdir); err != nil {
return err
}
@ -201,12 +180,12 @@ func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, outputPassphrase string
// ImportKeysZip imports keys from a zip file provided as an io.ReaderAt. The
// keys in the root_keys directory are left encrypted, but the other keys are
// decrypted with the specified passphrase.
func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string) error {
func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader) error {
// Temporarily store the keys in maps, so we can bail early if there's
// an error (for example, wrong passphrase), without leaving the key
// store in an inconsistent state
newRootKeys := make(map[string][]byte)
newNonRootKeys := make(map[string]data.PrivateKey)
newNonRootKeys := make(map[string][]byte)
// Note that using / as a separator is okay here - the zip package
// guarantees that the separator will be /
@ -222,7 +201,7 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
return err
}
pemBytes, err := ioutil.ReadAll(rc)
fileBytes, err := ioutil.ReadAll(rc)
if err != nil {
return nil
}
@ -231,25 +210,20 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
// Note that using / as a separator is okay here - the zip
// package guarantees that the separator will be /
if strings.HasPrefix(fNameTrimmed, rootKeysPrefix) {
if err = checkRootKeyIsEncrypted(pemBytes); err != nil {
if err = checkRootKeyIsEncrypted(fileBytes); err != nil {
rc.Close()
return err
}
// Root keys are preserved without decrypting
keyName := strings.TrimPrefix(fNameTrimmed, rootKeysPrefix)
newRootKeys[keyName] = pemBytes
newRootKeys[keyName] = fileBytes
} else if strings.HasPrefix(fNameTrimmed, nonRootKeysPrefix) {
// Non-root keys need to be decrypted
key, err := trustmanager.ParsePEMPrivateKey(pemBytes, passphrase)
if err != nil {
rc.Close()
return err
}
// Nonroot keys are preserved without decrypting
keyName := strings.TrimPrefix(fNameTrimmed, nonRootKeysPrefix)
newNonRootKeys[keyName] = key
newNonRootKeys[keyName] = fileBytes
} else {
// This path inside the zip archive doesn't look like a
// root key or a non-root key. To avoid adding a file
// root key, non-root key, or alias. To avoid adding a file
// to the filestore that we won't be able to use, skip
// this file in the import.
logrus.Warnf("skipping import of key with a path that doesn't begin with %s or %s: %s", rootKeysPrefix, nonRootKeysPrefix, f.Name)
@ -266,8 +240,8 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
}
}
for keyName, privKey := range newNonRootKeys {
if err := km.nonRootKeyStore.AddKey(keyName, privKey); err != nil {
for keyName, pemBytes := range newNonRootKeys {
if err := km.nonRootKeyStore.Add(keyName, pemBytes); err != nil {
return err
}
}
@ -275,39 +249,26 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader, passphrase string
return nil
}
func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun, outputPassphrase string) error {
func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun string) error {
// List all files but no symlinks
for _, f := range oldKeyStore.ListFiles(false) {
fullKeyPath := strings.TrimSpace(strings.TrimSuffix(f, filepath.Ext(f)))
relKeyPath := strings.TrimPrefix(fullKeyPath, oldKeyStore.BaseDir())
relKeyPath = strings.TrimPrefix(relKeyPath, string(filepath.Separator))
for _, relKeyPath := range oldKeyStore.ListKeys() {
// Skip keys that aren't associated with this GUN
if !strings.HasPrefix(relKeyPath, filepath.FromSlash(gun)) {
continue
}
pemBytes, err := oldKeyStore.Get(relKeyPath)
privKey, err := oldKeyStore.GetKey(relKeyPath)
if err != nil {
return err
}
block, _ := pem.Decode(pemBytes)
if block == nil {
return ErrNoValidPrivateKey
}
if x509.IsEncryptedPEMBlock(block) {
return ErrNonRootKeyEncrypted
}
// Key is not encrypted. Parse it, and add it
// to the temporary store as an encrypted key.
privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "")
alias, err := oldKeyStore.GetKeyAlias(relKeyPath)
if err != nil {
return err
}
err = newKeyStore.AddEncryptedKey(relKeyPath, privKey, outputPassphrase)
err = newKeyStore.AddKey(relKeyPath, alias, privKey)
if err != nil {
return err
}
@ -317,20 +278,22 @@ func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun, out
}
// ExportKeysByGUN exports all keys associated with a specified GUN to an
// io.Writer in zip format. outputPassphrase is the new passphrase to use to
// encrypt the keys. If blank, the keys will not be encrypted.
func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun, outputPassphrase string) error {
// io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to
// encrypt the keys.
func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever trustmanager.PassphraseRetriever) error {
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
defer os.RemoveAll(tempBaseDir)
privNonRootKeysSubdir := filepath.Join(privDir, nonRootKeysSubdir)
// Create temporary keystore to use as a staging area
tempNonRootKeysPath := filepath.Join(tempBaseDir, privDir, nonRootKeysSubdir)
tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath)
tempNonRootKeysPath := filepath.Join(tempBaseDir, privNonRootKeysSubdir)
tempNonRootKeyStore, err := trustmanager.NewKeyFileStore(tempNonRootKeysPath, passphraseRetriever)
if err != nil {
return err
}
if err := moveKeysByGUN(km.nonRootKeyStore, tempNonRootKeyStore, gun, outputPassphrase); err != nil {
if err := moveKeysByGUN(km.nonRootKeyStore, tempNonRootKeyStore, gun); err != nil {
return err
}
@ -340,7 +303,7 @@ func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun, outputPassphrase
return ErrNoKeysFoundForGUN
}
if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, tempBaseDir); err != nil {
if err := addKeysToArchive(zipWriter, tempNonRootKeyStore, privNonRootKeysSubdir); err != nil {
return err
}

View File

@ -37,10 +37,13 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
return ts, mux
}
var oldPassphrase = "oldPassphrase"
var exportPassphrase = "exportPassphrase"
var oldPassphraseRetriever = func(string, string, bool, int) (string, bool, error) { return oldPassphrase, false, nil }
var newPassphraseRetriever = func(string, string, bool, int) (string, bool, error) { return exportPassphrase, false, nil }
func TestImportExportZip(t *testing.T) {
gun := "docker.com/notary"
oldPassphrase := "oldPassphrase"
exportPassphrase := "exportPassphrase"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -51,13 +54,13 @@ func TestImportExportZip(t *testing.T) {
ts, _ := createTestServer(t)
defer ts.Close()
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), oldPassphrase)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, oldPassphrase)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -67,7 +70,7 @@ func TestImportExportZip(t *testing.T) {
tempZipFilePath := tempZipFile.Name()
defer os.Remove(tempZipFilePath)
err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, exportPassphrase)
err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, newPassphraseRetriever)
tempZipFile.Close()
assert.NoError(t, err)
@ -79,17 +82,20 @@ func TestImportExportZip(t *testing.T) {
passphraseByFile := make(map[string]string)
// Add non-root keys to the map. These should use the new passphrase
// because they were formerly unencrypted.
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(false)
// because the passwords were chosen by the newPassphraseRetriever.
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListKeys()
for _, privKeyName := range privKeyList {
relName := strings.TrimPrefix(privKeyName, tempBaseDir+string(filepath.Separator))
passphraseByFile[relName] = exportPassphrase
alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
assert.NoError(t, err, "privKey %s has no alias", privKeyName)
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
passphraseByFile[relKeyPath] = exportPassphrase
}
// Add root key to the map. This will use the old passphrase because it
// won't be reencrypted.
relRootKey := filepath.Join("private", "root_keys", rootCryptoService.ID()+".key")
passphraseByFile[relRootKey] = oldPassphrase
// Add root key to the map. This will use the export passphrase because it
// will be reencrypted.
relRootKey := filepath.Join("private", "root_keys", rootCryptoService.ID()+"_root.key")
passphraseByFile[relRootKey] = exportPassphrase
// Iterate through the files in the archive, checking that the files
// exist and are encrypted with the expected passphrase.
@ -126,13 +132,13 @@ func TestImportExportZip(t *testing.T) {
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport)
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), "oldPassphrase")
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2, "oldPassphrase")
rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo2.Initialize(rootCryptoService2)
@ -142,44 +148,33 @@ func TestImportExportZip(t *testing.T) {
zipReader, err = zip.OpenReader(tempZipFilePath)
assert.NoError(t, err, "could not open zip file")
// First try with an incorrect passphrase
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader, "wrongpassphrase")
// Don't use EqualError here because occasionally decrypting with the
// wrong passphrase returns a parse error
assert.Error(t, err)
zipReader.Close()
// Reopen the zip file for importing
zipReader, err = zip.OpenReader(tempZipFilePath)
assert.NoError(t, err, "could not open zip file")
// Now try with a valid passphrase. This time it should succeed.
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader, exportPassphrase)
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader)
assert.NoError(t, err)
zipReader.Close()
// Look for repo's keys in repo2
// Look for keys in private. The filenames should match the key IDs
// in the repo's private key store.
for _, privKeyName := range privKeyList {
privKeyRel := strings.TrimPrefix(privKeyName, tempBaseDir)
_, err := os.Stat(filepath.Join(tempBaseDir2, privKeyRel))
alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
assert.NoError(t, err, "privKey %s has no alias", privKeyName)
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
_, err = os.Stat(privKeyFileName)
assert.NoError(t, err, "missing private key: %s", privKeyName)
}
// Look for keys in root_keys
// There should be a file named after the key ID of the root key we
// passed in.
rootKeyFilename := rootCryptoService.ID() + ".key"
rootKeyFilename := rootCryptoService.ID() + "_root.key"
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
assert.NoError(t, err, "missing root key")
}
func TestImportExportGUN(t *testing.T) {
gun := "docker.com/notary"
oldPassphrase := "oldPassphrase"
exportPassphrase := "exportPassphrase"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -190,13 +185,13 @@ func TestImportExportGUN(t *testing.T) {
ts, _ := createTestServer(t)
defer ts.Close()
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), oldPassphrase)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, oldPassphrase)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -206,11 +201,11 @@ func TestImportExportGUN(t *testing.T) {
tempZipFilePath := tempZipFile.Name()
defer os.Remove(tempZipFilePath)
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, exportPassphrase)
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever)
assert.NoError(t, err)
// With an invalid GUN, this should return an error
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", exportPassphrase)
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", newPassphraseRetriever)
assert.EqualError(t, err, keystoremanager.ErrNoKeysFoundForGUN.Error())
tempZipFile.Close()
@ -224,15 +219,21 @@ func TestImportExportGUN(t *testing.T) {
// Add keys non-root keys to the map. These should use the new passphrase
// because they were formerly unencrypted.
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(false)
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListKeys()
for _, privKeyName := range privKeyList {
relName := strings.TrimPrefix(privKeyName, tempBaseDir+string(filepath.Separator))
passphraseByFile[relName] = exportPassphrase
alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
if err != nil {
t.Fatalf("privKey %s has no alias", privKeyName)
}
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
passphraseByFile[relKeyPath] = exportPassphrase
}
// Iterate through the files in the archive, checking that the files
// exist and are encrypted with the expected passphrase.
for _, f := range zipReader.File {
expectedPassphrase, present := passphraseByFile[f.Name]
if !present {
t.Fatalf("unexpected file %s in zip file", f.Name)
@ -265,13 +266,13 @@ func TestImportExportGUN(t *testing.T) {
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport)
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), "oldPassphrase")
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2, "oldPassphrase")
rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo2.Initialize(rootCryptoService2)
@ -281,36 +282,26 @@ func TestImportExportGUN(t *testing.T) {
zipReader, err = zip.OpenReader(tempZipFilePath)
assert.NoError(t, err, "could not open zip file")
// First try with an incorrect passphrase
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader, "wrongpassphrase")
// Don't use EqualError here because occasionally decrypting with the
// wrong passphrase returns a parse error
assert.Error(t, err)
zipReader.Close()
// Reopen the zip file for importing
zipReader, err = zip.OpenReader(tempZipFilePath)
assert.NoError(t, err, "could not open zip file")
// Now try with a valid passphrase. This time it should succeed.
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader, exportPassphrase)
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader)
assert.NoError(t, err)
zipReader.Close()
// Look for repo's non-root keys in repo2
// Look for keys in private. The filenames should match the key IDs
// in the repo's private key store.
for _, privKeyName := range privKeyList {
privKeyRel := strings.TrimPrefix(privKeyName, tempBaseDir)
_, err := os.Stat(filepath.Join(tempBaseDir2, privKeyRel))
assert.NoError(t, err, "missing private key: %s", privKeyName)
alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
if err != nil {
t.Fatalf("privKey %s has no alias", privKeyName)
}
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
_, err = os.Stat(privKeyFileName)
}
}
func TestImportExportRootKey(t *testing.T) {
gun := "docker.com/notary"
oldPassphrase := "oldPassphrase"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -321,13 +312,13 @@ func TestImportExportRootKey(t *testing.T) {
ts, _ := createTestServer(t)
defer ts.Close()
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), oldPassphrase)
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID, oldPassphrase)
rootCryptoService, err := repo.KeyStoreManager.GetRootCryptoService(rootKeyID)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService)
@ -347,13 +338,13 @@ func TestImportExportRootKey(t *testing.T) {
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport)
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
assert.NoError(t, err, "error creating repo: %s", err)
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String(), oldPassphrase)
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey.String())
assert.NoError(t, err, "error generating root key: %s", err)
rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2, oldPassphrase)
rootCryptoService2, err := repo2.KeyStoreManager.GetRootCryptoService(rootKeyID2)
assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo2.Initialize(rootCryptoService2)
@ -370,7 +361,7 @@ func TestImportExportRootKey(t *testing.T) {
// Look for repo's root key in repo2
// There should be a file named after the key ID of the root key we
// imported.
rootKeyFilename := rootKeyID + ".key"
rootKeyFilename := rootKeyID + "_root.key"
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
assert.NoError(t, err, "missing root key")

View File

@ -60,16 +60,16 @@ func (err ErrRootRotationFail) Error() string {
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error
// if it fails to create the KeyFileStores or load certificates
func NewKeyStoreManager(baseDir string) (*KeyStoreManager, error) {
func NewKeyStoreManager(baseDir string, passphraseRetriever trustmanager.PassphraseRetriever) (*KeyStoreManager, error) {
nonRootKeysPath := filepath.Join(baseDir, privDir, nonRootKeysSubdir)
nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath)
nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath, passphraseRetriever)
if err != nil {
return nil, err
}
// Load the keystore that will hold all of our encrypted Root Private Keys
rootKeysPath := filepath.Join(baseDir, privDir, rootKeysSubdir)
rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath)
rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath, passphraseRetriever)
if err != nil {
return nil, err
}
@ -142,8 +142,8 @@ func (km *KeyStoreManager) AddTrustedCACert(cert *x509.Certificate) {
km.trustedCAStore.AddCert(cert)
}
// GenRootKey generates a new root key protected by a given passphrase
func (km *KeyStoreManager) GenRootKey(algorithm, passphrase string) (string, error) {
// GenRootKey generates a new root key
func (km *KeyStoreManager) GenRootKey(algorithm string) (string, error) {
var err error
var privKey data.PrivateKey
@ -164,19 +164,20 @@ func (km *KeyStoreManager) GenRootKey(algorithm, passphrase string) (string, err
}
// Changing the root
km.rootKeyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase)
km.rootKeyStore.AddKey(privKey.ID(), "root", privKey)
return privKey.ID(), nil
}
// GetRootCryptoService retreives a root key and a cryptoservice to use with it
func (km *KeyStoreManager) GetRootCryptoService(rootKeyID, passphrase string) (*cryptoservice.UnlockedCryptoService, error) {
privKey, err := km.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase)
// GetRootCryptoService retrieves a root key and a cryptoservice to use with it
// TODO(mccauley): remove this as its no longer needed once we have key caching in the keystores
func (km *KeyStoreManager) GetRootCryptoService(rootKeyID string) (*cryptoservice.UnlockedCryptoService, error) {
privKey, err := km.rootKeyStore.GetKey(rootKeyID)
if err != nil {
return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err)
}
cryptoService := cryptoservice.NewCryptoService("", km.rootKeyStore, passphrase)
cryptoService := cryptoservice.NewCryptoService("", km.rootKeyStore)
return cryptoservice.NewUnlockedCryptoService(privKey, cryptoService), nil
}

View File

@ -23,12 +23,13 @@ import (
)
var (
server *httptest.Server
reader io.Reader
deleteKeyBaseURL string
createKeyBaseURL string
keyInfoBaseURL string
signBaseURL string
server *httptest.Server
reader io.Reader
deleteKeyBaseURL string
createKeyBaseURL string
keyInfoBaseURL string
signBaseURL string
passphraseRetriever = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil }
)
func SetupHSMEnv(t *testing.T) (*pkcs11.Ctx, pkcs11.SessionHandle) {
@ -73,8 +74,8 @@ func setup(cryptoServices signer.CryptoServiceIndex) {
}
func TestDeleteKeyHandlerReturns404WithNonexistentKey(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
@ -93,11 +94,12 @@ func TestDeleteKeyHandlerReturns404WithNonexistentKey(t *testing.T) {
}
func TestDeleteKeyHandler(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
tufKey, _ := cryptoService.Create("", data.ED25519Key)
assert.NotNil(t, tufKey)
requestJson, _ := json.Marshal(&pb.KeyID{ID: tufKey.ID()})
reader = strings.NewReader(string(requestJson))
@ -112,11 +114,12 @@ func TestDeleteKeyHandler(t *testing.T) {
}
func TestKeyInfoHandler(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
tufKey, _ := cryptoService.Create("", data.ED25519Key)
assert.NotNil(t, tufKey)
keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, tufKey.ID())
@ -140,8 +143,8 @@ func TestKeyInfoHandler(t *testing.T) {
func TestKeyInfoHandlerReturns404WithNonexistentKey(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
@ -185,8 +188,8 @@ func TestHSMCreateKeyHandler(t *testing.T) {
}
func TestSoftwareCreateKeyHandler(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.ED25519Key)
@ -243,8 +246,8 @@ func TestHSMSignHandler(t *testing.T) {
}
func TestSoftwareSignHandler(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
tufKey, err := cryptoService.Create("", data.ED25519Key)
@ -275,8 +278,8 @@ func TestSoftwareSignHandler(t *testing.T) {
}
func TestSoftwareSignWithInvalidRequestHandler(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
requestJson := "{\"blob\":\"7d16f1d0b95310a7bc557747fc4f20fcd41c1c5095ae42f189df0717e7d7f4a0a2b55debce630f43c4ac099769c612965e3fda3cd4c0078ee6a460f14fa19307\"}"
@ -299,8 +302,8 @@ func TestSoftwareSignWithInvalidRequestHandler(t *testing.T) {
}
func TestSignHandlerReturns404WithNonexistentKey(t *testing.T) {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"

View File

@ -24,11 +24,13 @@ var (
sClient pb.SignerClient
grpcServer *grpc.Server
void *pb.Void
pr trustmanager.PassphraseRetriever
)
func init() {
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
pr = func(string, string, bool, int) (string, bool, error) { return "passphrase", false, nil }
keyStore := trustmanager.NewKeyMemoryStore(pr)
cryptoService := cryptoservice.NewCryptoService("", keyStore)
cryptoServices := signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}
void = &pb.Void{}
//server setup

View File

@ -172,6 +172,11 @@ func (f *SimpleFileStore) list(path string, symlinks bool) []string {
matched, _ := filepath.Match("*"+f.fileExt, fi.Name())
if matched {
// Find the relative path for this file relative to the base path.
fp, err = filepath.Rel(path, fp)
if err != nil {
return err
}
files = append(files, fp)
}
return nil

View File

@ -4,6 +4,8 @@ import (
"path/filepath"
"strings"
"errors"
"fmt"
"github.com/endophage/gotuf/data"
)
@ -15,53 +17,55 @@ const (
type KeyStore interface {
LimitedFileStore
AddKey(name string, privKey data.PrivateKey) error
AddKey(name, alias string, privKey data.PrivateKey) error
GetKey(name string) (data.PrivateKey, error)
AddEncryptedKey(name string, privKey data.PrivateKey, passphrase string) error
GetDecryptedKey(name string, passphrase string) (data.PrivateKey, error)
GetKeyAlias(name string) (string, error)
ListKeys() []string
RemoveKey(name string) error
}
// PassphraseRetriever is a callback function that should retrieve a passphrase
// for a given named key. If it should be treated as new passphrase (e.g. with
// confirmation), createNew will be true. Attempts is passed in so that implementers
// decide how many chances to give to a human, for example.
type PassphraseRetriever func(keyId, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error)
// KeyFileStore persists and manages private keys on disk
type KeyFileStore struct {
SimpleFileStore
PassphraseRetriever
}
// KeyMemoryStore manages private keys in memory
type KeyMemoryStore struct {
MemoryFileStore
PassphraseRetriever
}
// NewKeyFileStore returns a new KeyFileStore creating a private directory to
// hold the keys.
func NewKeyFileStore(baseDir string) (*KeyFileStore, error) {
func NewKeyFileStore(baseDir string, passphraseRetriever PassphraseRetriever) (*KeyFileStore, error) {
fileStore, err := NewPrivateSimpleFileStore(baseDir, keyExtension)
if err != nil {
return nil, err
}
return &KeyFileStore{*fileStore}, nil
return &KeyFileStore{*fileStore, passphraseRetriever}, nil
}
// AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyFileStore) AddKey(name string, privKey data.PrivateKey) error {
return addKey(s, name, privKey)
func (s *KeyFileStore) AddKey(name, alias string, privKey data.PrivateKey) error {
return addKey(s, s.PassphraseRetriever, name, alias, privKey)
}
// GetKey returns the PrivateKey given a KeyID
func (s *KeyFileStore) GetKey(name string) (data.PrivateKey, error) {
return getKey(s, name)
return getKey(s, s.PassphraseRetriever, name)
}
// AddEncryptedKey stores the contents of a PEM-encoded private key as an encrypted PEM block
func (s *KeyFileStore) AddEncryptedKey(name string, privKey data.PrivateKey, passphrase string) error {
return addEncryptedKey(s, name, privKey, passphrase)
}
// GetDecryptedKey decrypts and returns the PEM Encoded private key given a flename
// and a passphrase
func (s *KeyFileStore) GetDecryptedKey(name string, passphrase string) (data.PrivateKey, error) {
return getDecryptedKey(s, name, passphrase)
// GetKeyAlias returns the PrivateKey's alias given a KeyID
func (s *KeyFileStore) GetKeyAlias(name string) (string, error) {
return getKeyAlias(s, name)
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
@ -71,32 +75,31 @@ func (s *KeyFileStore) ListKeys() []string {
return listKeys(s)
}
// RemoveKey removes the key from the keyfilestore
func (s *KeyFileStore) RemoveKey(name string) error {
return removeKey(s, name)
}
// NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
func NewKeyMemoryStore() *KeyMemoryStore {
func NewKeyMemoryStore(passphraseRetriever PassphraseRetriever) *KeyMemoryStore {
memStore := NewMemoryFileStore()
return &KeyMemoryStore{*memStore}
return &KeyMemoryStore{*memStore, passphraseRetriever}
}
// AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyMemoryStore) AddKey(name string, privKey data.PrivateKey) error {
return addKey(s, name, privKey)
func (s *KeyMemoryStore) AddKey(name, alias string, privKey data.PrivateKey) error {
return addKey(s, s.PassphraseRetriever, name, alias, privKey)
}
// GetKey returns the PrivateKey given a KeyID
func (s *KeyMemoryStore) GetKey(name string) (data.PrivateKey, error) {
return getKey(s, name)
return getKey(s, s.PassphraseRetriever, name)
}
// AddEncryptedKey stores the contents of a PEM-encoded private key as an encrypted PEM block
func (s *KeyMemoryStore) AddEncryptedKey(name string, privKey data.PrivateKey, passphrase string) error {
return addEncryptedKey(s, name, privKey, passphrase)
}
// GetDecryptedKey decrypts and returns the PEM Encoded private key given a flename
// and a passphrase
func (s *KeyMemoryStore) GetDecryptedKey(name string, passphrase string) (data.PrivateKey, error) {
return getDecryptedKey(s, name, passphrase)
// GetKeyAlias returns the PrivateKey's alias given a KeyID
func (s *KeyMemoryStore) GetKeyAlias(name string) (string, error) {
return getKeyAlias(s, name)
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
@ -106,64 +109,119 @@ func (s *KeyMemoryStore) ListKeys() []string {
return listKeys(s)
}
func addKey(s LimitedFileStore, name string, privKey data.PrivateKey) error {
// RemoveKey removes the key from the keystore
func (s *KeyMemoryStore) RemoveKey(name string) error {
return removeKey(s, name)
}
func addKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name, alias string, privKey data.PrivateKey) error {
pemPrivKey, err := KeyToPEM(privKey)
if err != nil {
return err
}
return s.Add(name, pemPrivKey)
attempts := 0
passphrase := ""
giveup := false
for {
passphrase, giveup, err = passphraseRetriever(name, alias, true, attempts)
if err != nil {
attempts++
continue
}
if giveup {
return errors.New("obtaining passphrase failed")
}
if attempts > 10 {
return errors.New("maximum number of passphrase attempts exceeded")
}
break
}
if passphrase != "" {
pemPrivKey, err = EncryptPrivateKey(privKey, passphrase)
if err != nil {
return err
}
}
return s.Add(name+"_"+alias, pemPrivKey)
}
func getKey(s LimitedFileStore, name string) (data.PrivateKey, error) {
keyBytes, err := s.Get(name)
func getKeyAlias(s LimitedFileStore, keyID string) (string, error) {
files := s.ListFiles(true)
name := strings.TrimSpace(strings.TrimSuffix(filepath.Base(keyID), filepath.Ext(keyID)))
for _, file := range files {
filename := filepath.Base(file)
if strings.HasPrefix(filename, name) {
aliasPlusDotKey := strings.TrimPrefix(filename, name+"_")
retVal := strings.TrimSuffix(aliasPlusDotKey, "."+keyExtension)
return retVal, nil
}
}
return "", fmt.Errorf("keyId %s has no alias", name)
}
// GetKey returns the PrivateKey given a KeyID
func getKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name string) (data.PrivateKey, error) {
keyAlias, err := getKeyAlias(s, name)
if err != nil {
return nil, err
}
// Convert PEM encoded bytes back to a PrivateKey
keyBytes, err := s.Get(name + "_" + keyAlias)
if err != nil {
return nil, err
}
// See if the key is encrypted. If its encrypted we'll fail to parse the private key
privKey, err := ParsePEMPrivateKey(keyBytes, "")
if err != nil {
return nil, err
}
return privKey, nil
}
func addEncryptedKey(s LimitedFileStore, name string, privKey data.PrivateKey, passphrase string) error {
encryptedPrivKey, err := EncryptPrivateKey(privKey, passphrase)
if err != nil {
return err
}
return s.Add(name, encryptedPrivKey)
}
func getDecryptedKey(s LimitedFileStore, name string, passphrase string) (data.PrivateKey, error) {
keyBytes, err := s.Get(name)
if err != nil {
return nil, err
}
// Gets an unencrypted PrivateKey.
privKey, err := ParsePEMPrivateKey(keyBytes, passphrase)
if err != nil {
return nil, err
}
// We need to decrypt the key, lets get a passphrase
for attempts := 0; ; attempts++ {
passphrase, giveup, err := passphraseRetriever(name, string(keyAlias), false, attempts)
// Check if the passphrase retriever got an error or if it is telling us to give up
if giveup || err != nil {
return nil, errors.New("obtaining passphrase failed")
}
if attempts > 10 {
return nil, errors.New("maximum number of passphrase attempts exceeded")
}
// Try to convert PEM encoded bytes back to a PrivateKey using the passphrase
privKey, err = ParsePEMPrivateKey(keyBytes, passphrase)
if err == nil {
// We managed to parse the PrivateKey. We've succeeded!
break
}
}
}
return privKey, nil
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
// There might be symlinks associating Certificate IDs to Public Keys, so this
// method only returns the IDs that aren't symlinks
func listKeys(s LimitedFileStore) []string {
var keyIDList []string
for _, f := range s.ListFiles(false) {
keyID := strings.TrimSpace(strings.TrimSuffix(filepath.Base(f), filepath.Ext(f)))
keyID := strings.TrimSpace(strings.TrimSuffix(f, filepath.Ext(f)))
keyID = keyID[:strings.LastIndex(keyID, "_")]
keyIDList = append(keyIDList, keyID)
}
return keyIDList
}
// RemoveKey removes the key from the keyfilestore
func (s *KeyFileStore) RemoveKey(name string) error {
return s.Remove(name)
func removeKey(s LimitedFileStore, name string) error {
keyAlias, err := getKeyAlias(s, name)
if err != nil {
return err
}
return s.Remove(name + "_" + keyAlias)
}

View File

@ -3,6 +3,7 @@ package trustmanager
import (
"bytes"
"crypto/rand"
"errors"
"io/ioutil"
"os"
"path/filepath"
@ -10,9 +11,18 @@ import (
"testing"
)
var passphraseRetriever = func(keyID string, alias string, createNew bool, numAttempts int) (string, bool, error) {
if numAttempts > 5 {
giveup := true
return "", giveup, errors.New("passPhraseRetriever failed after too many requests")
}
return "passphrase", false, nil
}
func TestAddKey(t *testing.T) {
testName := "docker.com/notary/root"
testExt := "key"
testAlias := "root"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -22,10 +32,10 @@ func TestAddKey(t *testing.T) {
defer os.RemoveAll(tempBaseDir)
// Since we're generating this manually we need to add the extension '.'
expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt)
expectedFilePath := filepath.Join(tempBaseDir, testName+"_"+testAlias+"."+testExt)
// Create our store
store, err := NewKeyFileStore(tempBaseDir)
store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
@ -36,7 +46,7 @@ func TestAddKey(t *testing.T) {
}
// Call the AddKey function
err = store.AddKey(testName, privKey)
err = store.AddKey(testName, "root", privKey)
if err != nil {
t.Fatalf("failed to add file to store: %v", err)
}
@ -83,8 +93,11 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
`)
testName := "docker.com/notary/root"
testExt := "key"
testAlias := "root"
perms := os.FileMode(0755)
emptyPassphraseRetriever := func(string, string, bool, int) (string, bool, error) { return "", false, nil }
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
if err != nil {
@ -93,7 +106,7 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
defer os.RemoveAll(tempBaseDir)
// Since we're generating this manually we need to add the extension '.'
filePath := filepath.Join(tempBaseDir, testName+"."+testExt)
filePath := filepath.Join(tempBaseDir, testName+"_"+testAlias+"."+testExt)
os.MkdirAll(filepath.Dir(filePath), perms)
if err = ioutil.WriteFile(filePath, testData, perms); err != nil {
@ -101,7 +114,7 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
}
// Create our store
store, err := NewKeyFileStore(tempBaseDir)
store, err := NewKeyFileStore(tempBaseDir, emptyPassphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
@ -124,9 +137,10 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
func TestAddGetKeyMemStore(t *testing.T) {
testName := "docker.com/notary/root"
testAlias := "root"
// Create our store
store := NewKeyMemoryStore()
store := NewKeyMemoryStore(passphraseRetriever)
privKey, err := GenerateRSAKey(rand.Reader, 512)
if err != nil {
@ -134,7 +148,7 @@ func TestAddGetKeyMemStore(t *testing.T) {
}
// Call the AddKey function
err = store.AddKey(testName, privKey)
err = store.AddKey(testName, testAlias, privKey)
if err != nil {
t.Fatalf("failed to add file to store: %v", err)
}
@ -145,62 +159,24 @@ func TestAddGetKeyMemStore(t *testing.T) {
t.Fatalf("failed to get key from store: %v", err)
}
// Check to see if alias exists
retrievedAlias, err := store.GetKeyAlias(testName)
if err != nil {
t.Fatalf("failed to get key from store: %v", err)
}
if retrievedAlias != testAlias {
t.Fatalf("retrievedAlias differs getAlias")
}
if !bytes.Equal(retrievedKey.Public(), privKey.Public()) ||
!bytes.Equal(retrievedKey.Private(), privKey.Private()) {
t.Fatalf("key contents differs after add/get")
}
}
func TestAddEncryptedAndGetDecrypted(t *testing.T) {
testExt := "key"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
if err != nil {
t.Fatalf("failed to create a temporary directory: %v", err)
}
defer os.RemoveAll(tempBaseDir)
// Create our FileStore
store, err := NewKeyFileStore(tempBaseDir)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
// Generate new PrivateKey
privKey, err := GenerateRSAKey(rand.Reader, 512)
if err != nil {
t.Fatalf("could not generate private key: %v", err)
}
// Call the AddEncryptedKey function
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica")
if err != nil {
t.Fatalf("failed to add file to store: %v", err)
}
// Since we're generating this manually we need to add the extension '.'
expectedFilePath := filepath.Join(tempBaseDir, privKey.ID()+"."+testExt)
// Check to see if file exists
_, err = ioutil.ReadFile(expectedFilePath)
if err != nil {
t.Fatalf("expected file not found: %v", err)
}
// Call the GetDecryptedKey function
readPrivKey, err := store.GetDecryptedKey(privKey.ID(), "diogomonica")
if err != nil {
t.Fatalf("could not decrypt private key: %v", err)
}
if !bytes.Equal(privKey.Private(), readPrivKey.Private()) {
t.Fatalf("written key and loaded key do not match")
}
}
func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
testExt := "key"
testAlias := "root"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -210,7 +186,7 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
defer os.RemoveAll(tempBaseDir)
// Create our FileStore
store, err := NewKeyFileStore(tempBaseDir)
store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
@ -222,13 +198,13 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
}
// Call the AddEncryptedKey function
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica")
err = store.AddKey(privKey.ID(), testAlias, privKey)
if err != nil {
t.Fatalf("failed to add file to store: %v", err)
}
// Since we're generating this manually we need to add the extension '.'
expectedFilePath := filepath.Join(tempBaseDir, privKey.ID()+"."+testExt)
expectedFilePath := filepath.Join(tempBaseDir, privKey.ID()+"_"+testAlias+"."+testExt)
// Get file description, open file
fp, err := os.OpenFile(expectedFilePath, os.O_WRONLY, 0600)
@ -240,13 +216,26 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
fp.WriteAt([]byte("a"), int64(1))
// Try to decrypt the file
_, err = store.GetDecryptedKey(privKey.ID(), "diogomonica")
_, err = store.GetKey(privKey.ID())
if err == nil {
t.Fatalf("expected error while decrypting the content due to invalid cipher text")
}
}
func TestGetDecryptedWithInvalidPassphrase(t *testing.T) {
// Make a passphraseRetriever that always returns a different passphrase in order to test
// decryption failure
a := "a"
var invalidPassphraseRetriever = func(keyId string, alias string, createNew bool, numAttempts int) (string, bool, error) {
if numAttempts > 5 {
giveup := true
return "", giveup, nil
}
a = a + a
return a, false, nil
}
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
if err != nil {
@ -255,14 +244,15 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) {
defer os.RemoveAll(tempBaseDir)
// Test with KeyFileStore
fileStore, err := NewKeyFileStore(tempBaseDir)
fileStore, err := NewKeyFileStore(tempBaseDir, invalidPassphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
testGetDecryptedWithInvalidPassphrase(t, fileStore)
// Test with KeyMemoryStore
memStore := NewKeyMemoryStore()
memStore := NewKeyMemoryStore(invalidPassphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key memorystore: %v", err)
}
@ -270,8 +260,40 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) {
}
func TestGetDecryptedWithConsistentlyInvalidPassphrase(t *testing.T) {
// Make a passphraseRetriever that always returns a different passphrase in order to test
// decryption failure
a := "aaaaaaaaaaaaa"
var consistentlyInvalidPassphraseRetriever = func(keyID string, alias string, createNew bool, numAttempts int) (string, bool, error) {
a = a + "a"
return a, false, nil
}
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
if err != nil {
t.Fatalf("failed to create a temporary directory: %v", err)
}
defer os.RemoveAll(tempBaseDir)
// Test with KeyFileStore
fileStore, err := NewKeyFileStore(tempBaseDir, consistentlyInvalidPassphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
testGetDecryptedWithInvalidPassphrase(t, fileStore)
// Test with KeyMemoryStore
memStore := NewKeyMemoryStore(consistentlyInvalidPassphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key memorystore: %v", err)
}
testGetDecryptedWithInvalidPassphrase(t, memStore)
}
func testGetDecryptedWithInvalidPassphrase(t *testing.T, store KeyStore) {
testName := "docker.com/notary/root"
testAlias := "root"
// Generate a new random RSA Key
privKey, err := GenerateRSAKey(rand.Reader, 512)
@ -279,14 +301,14 @@ func testGetDecryptedWithInvalidPassphrase(t *testing.T, store KeyStore) {
t.Fatalf("could not generate private key: %v", err)
}
// Call the AddEncryptedKey function
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica")
// Call the AddKey function
err = store.AddKey(privKey.ID(), testAlias, privKey)
if err != nil {
t.Fatalf("failed to add file to stoAFre: %v", err)
t.Fatalf("failed to add file to store: %v", err)
}
// Try to decrypt the file with an invalid passphrase
_, err = store.GetDecryptedKey(testName, "diegomonica")
_, err = store.GetKey(privKey.ID())
if err == nil {
t.Fatalf("expected error while decrypting the content due to invalid passphrase")
}
@ -295,6 +317,7 @@ func testGetDecryptedWithInvalidPassphrase(t *testing.T, store KeyStore) {
func TestRemoveKey(t *testing.T) {
testName := "docker.com/notary/root"
testExt := "key"
testAlias := "alias"
// Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -304,10 +327,10 @@ func TestRemoveKey(t *testing.T) {
defer os.RemoveAll(tempBaseDir)
// Since we're generating this manually we need to add the extension '.'
expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt)
expectedFilePath := filepath.Join(tempBaseDir, testName+"_"+testAlias+"."+testExt)
// Create our store
store, err := NewKeyFileStore(tempBaseDir)
store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
if err != nil {
t.Fatalf("failed to create new key filestore: %v", err)
}
@ -318,7 +341,7 @@ func TestRemoveKey(t *testing.T) {
}
// Call the AddKey function
err = store.AddKey(testName, privKey)
err = store.AddKey(testName, testAlias, privKey)
if err != nil {
t.Fatalf("failed to add file to store: %v", err)
}

View File

@ -67,7 +67,7 @@ func (s *X509FileStore) AddCert(cert *x509.Certificate) error {
return nil
}
// addNamedCert allows adding a certificate while controling the filename it gets
// addNamedCert allows adding a certificate while controlling the filename it gets
// stored under. If the file does not exist on disk, saves it.
func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error {
fileName, certID, err := fileName(cert)

View File

@ -314,7 +314,7 @@ func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey) (data.PrivateKey, error) {
return data.NewPrivateKey(data.RSAKey, rsaPubBytes, rsaPrivBytes), nil
}
// GenerateECDSAKey generates an ECDSA private key and returns a TUF PrivateKey
// GenerateECDSAKey generates an ECDSA Private key and returns a TUF PrivateKey
func GenerateECDSAKey(random io.Reader) (data.PrivateKey, error) {
ecdsaPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), random)
if err != nil {