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 cross
.cover .cover
*.swp *.swp
.idea
*.iml

View File

@ -49,7 +49,7 @@ test:
pwd: $BASE_STABLE pwd: $BASE_STABLE
# VET # 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 pwd: $BASE_STABLE
# LINT # LINT

View File

@ -29,8 +29,6 @@ const maxSize = 5 << 20
// notary repository // notary repository
type ErrRepoNotInitialized struct{} type ErrRepoNotInitialized struct{}
type passwordRetriever func() (string, error)
// ErrRepoNotInitialized is returned when trying to can publish on an uninitialized // ErrRepoNotInitialized is returned when trying to can publish on an uninitialized
// notary repository // notary repository
func (err *ErrRepoNotInitialized) Error() string { 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. // 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 // It takes the base directory under where all the trust files will be stored
// (usually ~/.docker/trust/). // (usually ~/.docker/trust/).
func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*NotaryRepository, error) { func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper,
keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir) passphraseRetriever trustmanager.PassphraseRetriever) (*NotaryRepository, error) {
keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir, passphraseRetriever)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cryptoService := cryptoservice.NewCryptoService(gun, keyStoreManager.NonRootKeyStore(), "") cryptoService := cryptoservice.NewCryptoService(gun, keyStoreManager.NonRootKeyStore())
nRepo := &NotaryRepository{ nRepo := &NotaryRepository{
gun: gun, 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 // is associated with. This is used to be able to retrieve the root private key
// associated with a particular certificate // associated with a particular certificate
logrus.Debugf("Linking %s to %s.", rootKey.ID(), uCryptoService.ID()) 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 { if err != nil {
return err 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 // Publish pushes the local changes in signed material to the remote notary-server
// Conceptually it performs an operation similar to a `git rebase` // 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 updateRoot bool
var root *data.Signed var root *data.Signed
// attempt to initialize the repo from the remote store // 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. // check if our root file is nearing expiry. Resign if it is.
if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty { if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty {
passphrase, err := getPass()
if err != nil { if err != nil {
return err return err
} }
rootKeyID := r.tufRepo.Root.Signed.Roles["root"].KeyIDs[0] 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 { if err != nil {
return err return err
} }

View File

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

View File

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

View File

@ -6,8 +6,7 @@
"type": "remote", "type": "remote",
"hostname": "notarysigner", "hostname": "notarysigner",
"port": "7899", "port": "7899",
"tls_ca_file": "./fixtures/root-ca.crt" "tls_ca_file": "./fixtures/root-ca.crt" },
},
"logging": { "logging": {
"level": 5 "level": 5
}, },

View File

@ -41,6 +41,12 @@ func init() {
flag.BoolVar(&debug, "debug", false, "show the version and exit") 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() { func main() {
flag.Usage = usage flag.Usage = usage
flag.Parse() flag.Parse()
@ -83,8 +89,8 @@ func main() {
cryptoServices[data.RSAKey] = api.NewRSAHardwareCryptoService(ctx, session) cryptoServices[data.RSAKey] = api.NewRSAHardwareCryptoService(ctx, session)
} }
keyStore := trustmanager.NewKeyMemoryStore() keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService("", keyStore, "") cryptoService := cryptoservice.NewCryptoService("", keyStore)
cryptoServices[data.ED25519Key] = cryptoService cryptoServices[data.ED25519Key] = cryptoService
cryptoServices[data.ECDSAKey] = cryptoService cryptoServices[data.ECDSAKey] = cryptoService

View File

@ -86,11 +86,11 @@ func init() {
fatalf("could not create Certificate X509FileStore: %v", err) 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 { if err != nil {
fatalf("could not create KeyFileStore: %v", err) fatalf("could not create KeyFileStore: %v", err)
} }
} }
func main() { func main() {

View File

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

View File

@ -24,13 +24,12 @@ const (
// operate on // operate on
type CryptoService struct { type CryptoService struct {
gun string gun string
passphrase string
keyStore trustmanager.KeyStore keyStore trustmanager.KeyStore
} }
// NewCryptoService returns an instance of CryptoService // NewCryptoService returns an instance of CryptoService
func NewCryptoService(gun string, keyStore trustmanager.KeyStore, passphrase string) *CryptoService { func NewCryptoService(gun string, keyStore trustmanager.KeyStore) *CryptoService {
return &CryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase} return &CryptoService{gun: gun, keyStore: keyStore}
} }
// Create is used to generate keys for targets, snapshots and timestamps // 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()) 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 // 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()), privKey) err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), role, privKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err) 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 // RemoveKey deletes a key by ID
func (ccs *CryptoService) RemoveKey(keyID string) error { 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 // 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 privKey data.PrivateKey
var err error 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 { 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 // InitRepo gets a signer that doesn't have access to
// the root keys. Continuing here is safe because we // the root keys. Continuing here is safe because we
// end up not returning any signatures. // 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) { func testCryptoService(t *testing.T, keyAlgo data.KeyAlgorithm, verifier signed.Verifier) {
content := []byte("this is a secret") content := []byte("this is a secret")
keyStore := trustmanager.NewKeyMemoryStore() keyStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cryptoService := NewCryptoService("", keyStore, "") cryptoService := NewCryptoService("", keyStore)
// Test Create // Test Create
tufKey, err := cryptoService.Create("", keyAlgo) tufKey, err := cryptoService.Create("", keyAlgo)

View File

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

View File

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

View File

@ -37,10 +37,13 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
return ts, mux 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) { func TestImportExportZip(t *testing.T) {
gun := "docker.com/notary" gun := "docker.com/notary"
oldPassphrase := "oldPassphrase"
exportPassphrase := "exportPassphrase"
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -51,13 +54,13 @@ func TestImportExportZip(t *testing.T) {
ts, _ := createTestServer(t) ts, _ := createTestServer(t)
defer ts.Close() 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) 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) 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) assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService) err = repo.Initialize(rootCryptoService)
@ -67,7 +70,7 @@ func TestImportExportZip(t *testing.T) {
tempZipFilePath := tempZipFile.Name() tempZipFilePath := tempZipFile.Name()
defer os.Remove(tempZipFilePath) defer os.Remove(tempZipFilePath)
err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, exportPassphrase) err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, newPassphraseRetriever)
tempZipFile.Close() tempZipFile.Close()
assert.NoError(t, err) assert.NoError(t, err)
@ -79,17 +82,20 @@ func TestImportExportZip(t *testing.T) {
passphraseByFile := make(map[string]string) passphraseByFile := make(map[string]string)
// Add non-root keys to the map. These should use the new passphrase // Add non-root keys to the map. These should use the new passphrase
// because they were formerly unencrypted. // because the passwords were chosen by the newPassphraseRetriever.
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(false) privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListKeys()
for _, privKeyName := range privKeyList { for _, privKeyName := range privKeyList {
relName := strings.TrimPrefix(privKeyName, tempBaseDir+string(filepath.Separator)) alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
passphraseByFile[relName] = exportPassphrase 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 // Add root key to the map. This will use the export passphrase because it
// won't be reencrypted. // will be reencrypted.
relRootKey := filepath.Join("private", "root_keys", rootCryptoService.ID()+".key") relRootKey := filepath.Join("private", "root_keys", rootCryptoService.ID()+"_root.key")
passphraseByFile[relRootKey] = oldPassphrase passphraseByFile[relRootKey] = exportPassphrase
// Iterate through the files in the archive, checking that the files // Iterate through the files in the archive, checking that the files
// exist and are encrypted with the expected passphrase. // 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) 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) 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) 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) assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo2.Initialize(rootCryptoService2) err = repo2.Initialize(rootCryptoService2)
@ -142,44 +148,33 @@ func TestImportExportZip(t *testing.T) {
zipReader, err = zip.OpenReader(tempZipFilePath) zipReader, err = zip.OpenReader(tempZipFilePath)
assert.NoError(t, err, "could not open zip file") 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. // 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) assert.NoError(t, err)
zipReader.Close() zipReader.Close()
// Look for repo's keys in repo2
// Look for keys in private. The filenames should match the key IDs // Look for keys in private. The filenames should match the key IDs
// in the repo's private key store. // in the repo's private key store.
for _, privKeyName := range privKeyList { for _, privKeyName := range privKeyList {
privKeyRel := strings.TrimPrefix(privKeyName, tempBaseDir) alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
_, err := os.Stat(filepath.Join(tempBaseDir2, privKeyRel)) 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) assert.NoError(t, err, "missing private key: %s", privKeyName)
} }
// Look for keys in root_keys // Look for keys in root_keys
// There should be a file named after the key ID of the root key we // There should be a file named after the key ID of the root key we
// passed in. // passed in.
rootKeyFilename := rootCryptoService.ID() + ".key" rootKeyFilename := rootCryptoService.ID() + "_root.key"
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
assert.NoError(t, err, "missing root key") assert.NoError(t, err, "missing root key")
} }
func TestImportExportGUN(t *testing.T) { func TestImportExportGUN(t *testing.T) {
gun := "docker.com/notary" gun := "docker.com/notary"
oldPassphrase := "oldPassphrase"
exportPassphrase := "exportPassphrase"
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -190,13 +185,13 @@ func TestImportExportGUN(t *testing.T) {
ts, _ := createTestServer(t) ts, _ := createTestServer(t)
defer ts.Close() 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) 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) 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) assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService) err = repo.Initialize(rootCryptoService)
@ -206,11 +201,11 @@ func TestImportExportGUN(t *testing.T) {
tempZipFilePath := tempZipFile.Name() tempZipFilePath := tempZipFile.Name()
defer os.Remove(tempZipFilePath) defer os.Remove(tempZipFilePath)
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, exportPassphrase) err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever)
assert.NoError(t, err) assert.NoError(t, err)
// With an invalid GUN, this should return an error // 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()) assert.EqualError(t, err, keystoremanager.ErrNoKeysFoundForGUN.Error())
tempZipFile.Close() 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 // Add keys non-root keys to the map. These should use the new passphrase
// because they were formerly unencrypted. // because they were formerly unencrypted.
privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListFiles(false) privKeyList := repo.KeyStoreManager.NonRootKeyStore().ListKeys()
for _, privKeyName := range privKeyList { for _, privKeyName := range privKeyList {
relName := strings.TrimPrefix(privKeyName, tempBaseDir+string(filepath.Separator)) alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
passphraseByFile[relName] = exportPassphrase 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 // Iterate through the files in the archive, checking that the files
// exist and are encrypted with the expected passphrase. // exist and are encrypted with the expected passphrase.
for _, f := range zipReader.File { for _, f := range zipReader.File {
expectedPassphrase, present := passphraseByFile[f.Name] expectedPassphrase, present := passphraseByFile[f.Name]
if !present { if !present {
t.Fatalf("unexpected file %s in zip file", f.Name) 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) 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) 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) 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) assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo2.Initialize(rootCryptoService2) err = repo2.Initialize(rootCryptoService2)
@ -281,36 +282,26 @@ func TestImportExportGUN(t *testing.T) {
zipReader, err = zip.OpenReader(tempZipFilePath) zipReader, err = zip.OpenReader(tempZipFilePath)
assert.NoError(t, err, "could not open zip file") 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. // 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) assert.NoError(t, err)
zipReader.Close() zipReader.Close()
// Look for repo's non-root keys in repo2
// Look for keys in private. The filenames should match the key IDs // Look for keys in private. The filenames should match the key IDs
// in the repo's private key store. // in the repo's private key store.
for _, privKeyName := range privKeyList { for _, privKeyName := range privKeyList {
privKeyRel := strings.TrimPrefix(privKeyName, tempBaseDir) alias, err := repo.KeyStoreManager.NonRootKeyStore().GetKeyAlias(privKeyName)
_, err := os.Stat(filepath.Join(tempBaseDir2, privKeyRel)) if err != nil {
assert.NoError(t, err, "missing private key: %s", privKeyName) 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) { func TestImportExportRootKey(t *testing.T) {
gun := "docker.com/notary" gun := "docker.com/notary"
oldPassphrase := "oldPassphrase"
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -321,13 +312,13 @@ func TestImportExportRootKey(t *testing.T) {
ts, _ := createTestServer(t) ts, _ := createTestServer(t)
defer ts.Close() 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) 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) 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) assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo.Initialize(rootCryptoService) 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) 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) 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) 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) assert.NoError(t, err, "error retrieving root key: %s", err)
err = repo2.Initialize(rootCryptoService2) err = repo2.Initialize(rootCryptoService2)
@ -370,7 +361,7 @@ func TestImportExportRootKey(t *testing.T) {
// Look for repo's root key in repo2 // Look for repo's root key in repo2
// There should be a file named after the key ID of the root key we // There should be a file named after the key ID of the root key we
// imported. // imported.
rootKeyFilename := rootKeyID + ".key" rootKeyFilename := rootKeyID + "_root.key"
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename)) _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
assert.NoError(t, err, "missing root key") 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 // NewKeyStoreManager returns an initialized KeyStoreManager, or an error
// if it fails to create the KeyFileStores or load certificates // 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) nonRootKeysPath := filepath.Join(baseDir, privDir, nonRootKeysSubdir)
nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath) nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath, passphraseRetriever)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Load the keystore that will hold all of our encrypted Root Private Keys // Load the keystore that will hold all of our encrypted Root Private Keys
rootKeysPath := filepath.Join(baseDir, privDir, rootKeysSubdir) rootKeysPath := filepath.Join(baseDir, privDir, rootKeysSubdir)
rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath) rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath, passphraseRetriever)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -142,8 +142,8 @@ func (km *KeyStoreManager) AddTrustedCACert(cert *x509.Certificate) {
km.trustedCAStore.AddCert(cert) km.trustedCAStore.AddCert(cert)
} }
// GenRootKey generates a new root key protected by a given passphrase // GenRootKey generates a new root key
func (km *KeyStoreManager) GenRootKey(algorithm, passphrase string) (string, error) { func (km *KeyStoreManager) GenRootKey(algorithm string) (string, error) {
var err error var err error
var privKey data.PrivateKey var privKey data.PrivateKey
@ -164,19 +164,20 @@ func (km *KeyStoreManager) GenRootKey(algorithm, passphrase string) (string, err
} }
// Changing the root // Changing the root
km.rootKeyStore.AddEncryptedKey(privKey.ID(), privKey, passphrase) km.rootKeyStore.AddKey(privKey.ID(), "root", privKey)
return privKey.ID(), nil return privKey.ID(), nil
} }
// GetRootCryptoService retreives a root key and a cryptoservice to use with it // GetRootCryptoService retrieves a root key and a cryptoservice to use with it
func (km *KeyStoreManager) GetRootCryptoService(rootKeyID, passphrase string) (*cryptoservice.UnlockedCryptoService, error) { // TODO(mccauley): remove this as its no longer needed once we have key caching in the keystores
privKey, err := km.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase) func (km *KeyStoreManager) GetRootCryptoService(rootKeyID string) (*cryptoservice.UnlockedCryptoService, error) {
privKey, err := km.rootKeyStore.GetKey(rootKeyID)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err) 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 return cryptoservice.NewUnlockedCryptoService(privKey, cryptoService), nil
} }

View File

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

View File

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

View File

@ -172,6 +172,11 @@ func (f *SimpleFileStore) list(path string, symlinks bool) []string {
matched, _ := filepath.Match("*"+f.fileExt, fi.Name()) matched, _ := filepath.Match("*"+f.fileExt, fi.Name())
if matched { 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) files = append(files, fp)
} }
return nil return nil

View File

@ -4,6 +4,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"errors"
"fmt"
"github.com/endophage/gotuf/data" "github.com/endophage/gotuf/data"
) )
@ -15,53 +17,55 @@ const (
type KeyStore interface { type KeyStore interface {
LimitedFileStore LimitedFileStore
AddKey(name string, privKey data.PrivateKey) error AddKey(name, alias string, privKey data.PrivateKey) error
GetKey(name string) (data.PrivateKey, error) GetKey(name string) (data.PrivateKey, error)
AddEncryptedKey(name string, privKey data.PrivateKey, passphrase string) error GetKeyAlias(name string) (string, error)
GetDecryptedKey(name string, passphrase string) (data.PrivateKey, error)
ListKeys() []string 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 // KeyFileStore persists and manages private keys on disk
type KeyFileStore struct { type KeyFileStore struct {
SimpleFileStore SimpleFileStore
PassphraseRetriever
} }
// KeyMemoryStore manages private keys in memory // KeyMemoryStore manages private keys in memory
type KeyMemoryStore struct { type KeyMemoryStore struct {
MemoryFileStore MemoryFileStore
PassphraseRetriever
} }
// NewKeyFileStore returns a new KeyFileStore creating a private directory to // NewKeyFileStore returns a new KeyFileStore creating a private directory to
// hold the keys. // hold the keys.
func NewKeyFileStore(baseDir string) (*KeyFileStore, error) { func NewKeyFileStore(baseDir string, passphraseRetriever PassphraseRetriever) (*KeyFileStore, error) {
fileStore, err := NewPrivateSimpleFileStore(baseDir, keyExtension) fileStore, err := NewPrivateSimpleFileStore(baseDir, keyExtension)
if err != nil { if err != nil {
return nil, err 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 // AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyFileStore) AddKey(name string, privKey data.PrivateKey) error { func (s *KeyFileStore) AddKey(name, alias string, privKey data.PrivateKey) error {
return addKey(s, name, privKey) return addKey(s, s.PassphraseRetriever, name, alias, privKey)
} }
// GetKey returns the PrivateKey given a KeyID // GetKey returns the PrivateKey given a KeyID
func (s *KeyFileStore) GetKey(name string) (data.PrivateKey, error) { 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 // GetKeyAlias returns the PrivateKey's alias given a KeyID
func (s *KeyFileStore) AddEncryptedKey(name string, privKey data.PrivateKey, passphrase string) error { func (s *KeyFileStore) GetKeyAlias(name string) (string, error) {
return addEncryptedKey(s, name, privKey, passphrase) return getKeyAlias(s, name)
}
// 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)
} }
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore. // ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
@ -71,32 +75,31 @@ func (s *KeyFileStore) ListKeys() []string {
return listKeys(s) 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 // NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
func NewKeyMemoryStore() *KeyMemoryStore { func NewKeyMemoryStore(passphraseRetriever PassphraseRetriever) *KeyMemoryStore {
memStore := NewMemoryFileStore() memStore := NewMemoryFileStore()
return &KeyMemoryStore{*memStore} return &KeyMemoryStore{*memStore, passphraseRetriever}
} }
// AddKey stores the contents of a PEM-encoded private key as a PEM block // AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyMemoryStore) AddKey(name string, privKey data.PrivateKey) error { func (s *KeyMemoryStore) AddKey(name, alias string, privKey data.PrivateKey) error {
return addKey(s, name, privKey) return addKey(s, s.PassphraseRetriever, name, alias, privKey)
} }
// GetKey returns the PrivateKey given a KeyID // GetKey returns the PrivateKey given a KeyID
func (s *KeyMemoryStore) GetKey(name string) (data.PrivateKey, error) { 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 // GetKeyAlias returns the PrivateKey's alias given a KeyID
func (s *KeyMemoryStore) AddEncryptedKey(name string, privKey data.PrivateKey, passphrase string) error { func (s *KeyMemoryStore) GetKeyAlias(name string) (string, error) {
return addEncryptedKey(s, name, privKey, passphrase) return getKeyAlias(s, name)
}
// 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)
} }
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore. // ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
@ -106,64 +109,119 @@ func (s *KeyMemoryStore) ListKeys() []string {
return listKeys(s) 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) pemPrivKey, err := KeyToPEM(privKey)
if err != nil { if err != nil {
return err return err
} }
return s.Add(name, pemPrivKey) attempts := 0
} passphrase := ""
giveup := false
func getKey(s LimitedFileStore, name string) (data.PrivateKey, error) { for {
keyBytes, err := s.Get(name) passphrase, giveup, err = passphraseRetriever(name, alias, true, attempts)
if err != nil { if err != nil {
return nil, err attempts++
continue
}
if giveup {
return errors.New("obtaining passphrase failed")
}
if attempts > 10 {
return errors.New("maximum number of passphrase attempts exceeded")
}
break
} }
// Convert PEM encoded bytes back to a PrivateKey if passphrase != "" {
privKey, err := ParsePEMPrivateKey(keyBytes, "") pemPrivKey, err = EncryptPrivateKey(privKey, passphrase)
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 { if err != nil {
return err return err
} }
return s.Add(name, encryptedPrivKey)
} }
func getDecryptedKey(s LimitedFileStore, name string, passphrase string) (data.PrivateKey, error) { return s.Add(name+"_"+alias, pemPrivKey)
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 { if err != nil {
return nil, err return nil, err
} }
// Gets an unencrypted PrivateKey. keyBytes, err := s.Get(name + "_" + keyAlias)
privKey, err := ParsePEMPrivateKey(keyBytes, passphrase)
if err != nil { if err != nil {
return nil, err 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 {
// 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 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 { func listKeys(s LimitedFileStore) []string {
var keyIDList []string var keyIDList []string
for _, f := range s.ListFiles(false) { 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) keyIDList = append(keyIDList, keyID)
} }
return keyIDList return keyIDList
} }
// RemoveKey removes the key from the keyfilestore // RemoveKey removes the key from the keyfilestore
func (s *KeyFileStore) RemoveKey(name string) error { func removeKey(s LimitedFileStore, name string) error {
return s.Remove(name) keyAlias, err := getKeyAlias(s, name)
if err != nil {
return err
}
return s.Remove(name + "_" + keyAlias)
} }

View File

@ -3,6 +3,7 @@ package trustmanager
import ( import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"errors"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -10,9 +11,18 @@ import (
"testing" "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) { func TestAddKey(t *testing.T) {
testName := "docker.com/notary/root" testName := "docker.com/notary/root"
testExt := "key" testExt := "key"
testAlias := "root"
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -22,10 +32,10 @@ func TestAddKey(t *testing.T) {
defer os.RemoveAll(tempBaseDir) defer os.RemoveAll(tempBaseDir)
// Since we're generating this manually we need to add the extension '.' // 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 // Create our store
store, err := NewKeyFileStore(tempBaseDir) store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
if err != nil { if err != nil {
t.Fatalf("failed to create new key filestore: %v", err) t.Fatalf("failed to create new key filestore: %v", err)
} }
@ -36,7 +46,7 @@ func TestAddKey(t *testing.T) {
} }
// Call the AddKey function // Call the AddKey function
err = store.AddKey(testName, privKey) err = store.AddKey(testName, "root", privKey)
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) t.Fatalf("failed to add file to store: %v", err)
} }
@ -83,8 +93,11 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
`) `)
testName := "docker.com/notary/root" testName := "docker.com/notary/root"
testExt := "key" testExt := "key"
testAlias := "root"
perms := os.FileMode(0755) perms := os.FileMode(0755)
emptyPassphraseRetriever := func(string, string, bool, int) (string, bool, error) { return "", false, nil }
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
if err != nil { if err != nil {
@ -93,7 +106,7 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
defer os.RemoveAll(tempBaseDir) defer os.RemoveAll(tempBaseDir)
// Since we're generating this manually we need to add the extension '.' // 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) os.MkdirAll(filepath.Dir(filePath), perms)
if err = ioutil.WriteFile(filePath, testData, perms); err != nil { if err = ioutil.WriteFile(filePath, testData, perms); err != nil {
@ -101,7 +114,7 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
} }
// Create our store // Create our store
store, err := NewKeyFileStore(tempBaseDir) store, err := NewKeyFileStore(tempBaseDir, emptyPassphraseRetriever)
if err != nil { if err != nil {
t.Fatalf("failed to create new key filestore: %v", err) t.Fatalf("failed to create new key filestore: %v", err)
} }
@ -124,9 +137,10 @@ EMl3eFOJXjIch/wIesRSN+2dGOsl7neercjMh1i9RvpCwHDx/E0=
func TestAddGetKeyMemStore(t *testing.T) { func TestAddGetKeyMemStore(t *testing.T) {
testName := "docker.com/notary/root" testName := "docker.com/notary/root"
testAlias := "root"
// Create our store // Create our store
store := NewKeyMemoryStore() store := NewKeyMemoryStore(passphraseRetriever)
privKey, err := GenerateRSAKey(rand.Reader, 512) privKey, err := GenerateRSAKey(rand.Reader, 512)
if err != nil { if err != nil {
@ -134,7 +148,7 @@ func TestAddGetKeyMemStore(t *testing.T) {
} }
// Call the AddKey function // Call the AddKey function
err = store.AddKey(testName, privKey) err = store.AddKey(testName, testAlias, privKey)
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) 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) 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()) || if !bytes.Equal(retrievedKey.Public(), privKey.Public()) ||
!bytes.Equal(retrievedKey.Private(), privKey.Private()) { !bytes.Equal(retrievedKey.Private(), privKey.Private()) {
t.Fatalf("key contents differs after add/get") 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) { func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
testExt := "key" testExt := "key"
testAlias := "root"
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -210,7 +186,7 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
defer os.RemoveAll(tempBaseDir) defer os.RemoveAll(tempBaseDir)
// Create our FileStore // Create our FileStore
store, err := NewKeyFileStore(tempBaseDir) store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
if err != nil { if err != nil {
t.Fatalf("failed to create new key filestore: %v", err) t.Fatalf("failed to create new key filestore: %v", err)
} }
@ -222,13 +198,13 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
} }
// Call the AddEncryptedKey function // Call the AddEncryptedKey function
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica") err = store.AddKey(privKey.ID(), testAlias, privKey)
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) t.Fatalf("failed to add file to store: %v", err)
} }
// Since we're generating this manually we need to add the extension '.' // 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 // Get file description, open file
fp, err := os.OpenFile(expectedFilePath, os.O_WRONLY, 0600) fp, err := os.OpenFile(expectedFilePath, os.O_WRONLY, 0600)
@ -240,13 +216,26 @@ func TestGetDecryptedWithTamperedCipherText(t *testing.T) {
fp.WriteAt([]byte("a"), int64(1)) fp.WriteAt([]byte("a"), int64(1))
// Try to decrypt the file // Try to decrypt the file
_, err = store.GetDecryptedKey(privKey.ID(), "diogomonica") _, err = store.GetKey(privKey.ID())
if err == nil { if err == nil {
t.Fatalf("expected error while decrypting the content due to invalid cipher text") t.Fatalf("expected error while decrypting the content due to invalid cipher text")
} }
} }
func TestGetDecryptedWithInvalidPassphrase(t *testing.T) { 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 // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
if err != nil { if err != nil {
@ -255,14 +244,15 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) {
defer os.RemoveAll(tempBaseDir) defer os.RemoveAll(tempBaseDir)
// Test with KeyFileStore // Test with KeyFileStore
fileStore, err := NewKeyFileStore(tempBaseDir) fileStore, err := NewKeyFileStore(tempBaseDir, invalidPassphraseRetriever)
if err != nil { if err != nil {
t.Fatalf("failed to create new key filestore: %v", err) t.Fatalf("failed to create new key filestore: %v", err)
} }
testGetDecryptedWithInvalidPassphrase(t, fileStore) testGetDecryptedWithInvalidPassphrase(t, fileStore)
// Test with KeyMemoryStore // Test with KeyMemoryStore
memStore := NewKeyMemoryStore() memStore := NewKeyMemoryStore(invalidPassphraseRetriever)
if err != nil { if err != nil {
t.Fatalf("failed to create new key memorystore: %v", err) 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) { func testGetDecryptedWithInvalidPassphrase(t *testing.T, store KeyStore) {
testName := "docker.com/notary/root" testAlias := "root"
// Generate a new random RSA Key // Generate a new random RSA Key
privKey, err := GenerateRSAKey(rand.Reader, 512) 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) t.Fatalf("could not generate private key: %v", err)
} }
// Call the AddEncryptedKey function // Call the AddKey function
err = store.AddEncryptedKey(privKey.ID(), privKey, "diogomonica") err = store.AddKey(privKey.ID(), testAlias, privKey)
if err != nil { 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 // Try to decrypt the file with an invalid passphrase
_, err = store.GetDecryptedKey(testName, "diegomonica") _, err = store.GetKey(privKey.ID())
if err == nil { if err == nil {
t.Fatalf("expected error while decrypting the content due to invalid passphrase") 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) { func TestRemoveKey(t *testing.T) {
testName := "docker.com/notary/root" testName := "docker.com/notary/root"
testExt := "key" testExt := "key"
testAlias := "alias"
// Temporary directory where test files will be created // Temporary directory where test files will be created
tempBaseDir, err := ioutil.TempDir("", "notary-test-") tempBaseDir, err := ioutil.TempDir("", "notary-test-")
@ -304,10 +327,10 @@ func TestRemoveKey(t *testing.T) {
defer os.RemoveAll(tempBaseDir) defer os.RemoveAll(tempBaseDir)
// Since we're generating this manually we need to add the extension '.' // 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 // Create our store
store, err := NewKeyFileStore(tempBaseDir) store, err := NewKeyFileStore(tempBaseDir, passphraseRetriever)
if err != nil { if err != nil {
t.Fatalf("failed to create new key filestore: %v", err) t.Fatalf("failed to create new key filestore: %v", err)
} }
@ -318,7 +341,7 @@ func TestRemoveKey(t *testing.T) {
} }
// Call the AddKey function // Call the AddKey function
err = store.AddKey(testName, privKey) err = store.AddKey(testName, testAlias, privKey)
if err != nil { if err != nil {
t.Fatalf("failed to add file to store: %v", err) 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 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. // stored under. If the file does not exist on disk, saves it.
func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error { func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error {
fileName, certID, err := fileName(cert) 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 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) { func GenerateECDSAKey(random io.Reader) (data.PrivateKey, error) {
ecdsaPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), random) ecdsaPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), random)
if err != nil { if err != nil {