mirror of https://github.com/docker/docs.git
Merge pull request #72 from docker/passphrase_callbacks
Update keyfilestore to use passwordRetriever
This commit is contained in:
commit
54b312754f
|
@ -4,3 +4,5 @@ bin
|
||||||
cross
|
cross
|
||||||
.cover
|
.cover
|
||||||
*.swp
|
*.swp
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue