mirror of https://github.com/docker/docs.git
move import/export to cryptoservice and add import to yubikey
Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
parent
2cb072667c
commit
be4c0669c1
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/keystoremanager"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/store"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/keystoremanager"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/signer/api"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/store"
|
||||
|
|
|
|||
|
|
@ -131,20 +131,6 @@ func main() {
|
|||
|
||||
cryptoServices := make(signer.CryptoServiceIndex)
|
||||
|
||||
pin := mainViper.GetString(pinCode)
|
||||
pkcs11Lib := mainViper.GetString("crypto.pkcs11lib")
|
||||
if pkcs11Lib != "" {
|
||||
if pin == "" {
|
||||
log.Fatalf("Using PIN is mandatory with pkcs11")
|
||||
}
|
||||
|
||||
ctx, session := SetupHSMEnv(pkcs11Lib, pin)
|
||||
|
||||
defer cleanup(ctx, session)
|
||||
|
||||
cryptoServices[data.RSAKey] = api.NewRSAHardwareCryptoService(ctx, session)
|
||||
}
|
||||
|
||||
configDBType := strings.ToLower(mainViper.GetString("storage.backend"))
|
||||
dbURL := mainViper.GetString("storage.db_url")
|
||||
if configDBType != dbType || dbURL == "" {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ import (
|
|||
"strings"
|
||||
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/keystoremanager"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -117,10 +119,7 @@ func keysRemoveKey(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
keyID := args[0]
|
||||
|
||||
|
|
@ -141,25 +140,8 @@ func keysRemoveKey(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Choose the correct filestore to remove the key from
|
||||
keyMap := keyStoreManager.KeyStore.ListKeys()
|
||||
|
||||
// Attempt to find the full GUN to the key in the map
|
||||
// This is irrelevant for removing root keys, but does no harm
|
||||
var keyWithGUN string
|
||||
for k := range keyMap {
|
||||
if filepath.Base(k) == keyID {
|
||||
keyWithGUN = k
|
||||
}
|
||||
}
|
||||
|
||||
// If empty, we didn't find any matches
|
||||
if keyWithGUN == "" {
|
||||
fatalf("key with key ID: %s not found\n", keyID)
|
||||
}
|
||||
|
||||
// Attempt to remove the key
|
||||
err = keyStoreManager.KeyStore.RemoveKey(keyWithGUN)
|
||||
err = cs.RemoveKey(keyID)
|
||||
if err != nil {
|
||||
fatalf("failed to remove key with key ID: %s, %v", keyID, err)
|
||||
}
|
||||
|
|
@ -178,13 +160,10 @@ func keysList(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
// Get a map of all the keys/roles
|
||||
keysMap := keyStoreManager.KeyStore.ListKeys()
|
||||
keysMap := cs.ListAllKeys()
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("# Root keys: ")
|
||||
|
|
@ -236,17 +215,14 @@ func keysGenerateRootKey(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
keyID, err := keyStoreManager.GenRootKey(algorithm)
|
||||
pubKey, err := cs.Create(data.CanonicalRootRole, algorithm)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new root key: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Generated new %s key with keyID: %s\n", algorithm, keyID)
|
||||
fmt.Printf("Generated new %s key with keyID: %s\n", algorithm, pubKey.ID())
|
||||
}
|
||||
|
||||
// keysExport exports a collection of keys to a ZIP file
|
||||
|
|
@ -265,10 +241,7 @@ func keysExport(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
exportFile, err := os.Create(exportFilename)
|
||||
if err != nil {
|
||||
|
|
@ -279,9 +252,9 @@ func keysExport(cmd *cobra.Command, args []string) {
|
|||
// unlocking passphrase and reusing that.
|
||||
exportRetriever := passphrase.PromptRetriever()
|
||||
if keysExportGUN != "" {
|
||||
err = keyStoreManager.ExportKeysByGUN(exportFile, keysExportGUN, exportRetriever)
|
||||
err = cs.ExportKeysByGUN(exportFile, keysExportGUN, exportRetriever)
|
||||
} else {
|
||||
err = keyStoreManager.ExportAllKeys(exportFile, exportRetriever)
|
||||
err = cs.ExportAllKeys(exportFile, exportRetriever)
|
||||
}
|
||||
|
||||
exportFile.Close()
|
||||
|
|
@ -313,10 +286,7 @@ func keysExportRoot(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
exportFile, err := os.Create(exportFilename)
|
||||
if err != nil {
|
||||
|
|
@ -326,9 +296,9 @@ func keysExportRoot(cmd *cobra.Command, args []string) {
|
|||
// Must use a different passphrase retriever to avoid caching the
|
||||
// unlocking passphrase and reusing that.
|
||||
exportRetriever := passphrase.PromptRetriever()
|
||||
err = keyStoreManager.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever)
|
||||
err = cs.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever)
|
||||
} else {
|
||||
err = keyStoreManager.ExportRootKey(exportFile, keyID)
|
||||
err = cs.ExportRootKey(exportFile, keyID)
|
||||
}
|
||||
exportFile.Close()
|
||||
if err != nil {
|
||||
|
|
@ -353,10 +323,7 @@ func keysImport(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
zipReader, err := zip.OpenReader(importFilename)
|
||||
if err != nil {
|
||||
|
|
@ -364,7 +331,7 @@ func keysImport(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
defer zipReader.Close()
|
||||
|
||||
err = keyStoreManager.ImportKeysZip(zipReader.Reader)
|
||||
err = cs.ImportKeysZip(zipReader.Reader)
|
||||
|
||||
if err != nil {
|
||||
fatalf("error importing keys: %v", err)
|
||||
|
|
@ -387,10 +354,7 @@ func keysImportRoot(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf("failed to create private key store in directory: %s", keysPath)
|
||||
}
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
|
||||
if err != nil {
|
||||
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", fileKeyStore)
|
||||
|
||||
importFile, err := os.Open(importFilename)
|
||||
if err != nil {
|
||||
|
|
@ -398,7 +362,7 @@ func keysImportRoot(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
defer importFile.Close()
|
||||
|
||||
err = keyStoreManager.ImportRootKey(importFile)
|
||||
err = cs.ImportRootKey(importFile)
|
||||
|
||||
if err != nil {
|
||||
fatalf("error importing root key: %v", err)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/version"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func NewCryptoService(gun string, keyStores ...trustmanager.KeyStore) *CryptoSer
|
|||
}
|
||||
|
||||
// Create is used to generate keys for targets, snapshots and timestamps
|
||||
func (ccs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) {
|
||||
func (cs *CryptoService) Create(role, algorithm string) (data.PublicKey, error) {
|
||||
var privKey data.PrivateKey
|
||||
var err error
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ func (ccs *CryptoService) RemoveKey(keyID string) (err error) {
|
|||
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
|
||||
// errors to sign and expects the called to validate if the number of returned
|
||||
// signatures is adequate.
|
||||
func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
|
||||
func (cs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
|
||||
signatures := make([]data.Signature, 0, len(keyIDs))
|
||||
for _, keyid := range keyIDs {
|
||||
keyName := keyid
|
||||
|
|
@ -152,9 +152,9 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
|
|||
}
|
||||
|
||||
// ListKeys returns a list of key IDs valid for the given role
|
||||
func (ccs *CryptoService) ListKeys(role string) []string {
|
||||
func (cs *CryptoService) ListKeys(role string) []string {
|
||||
var res []string
|
||||
for _, ks := range ccs.keyStores {
|
||||
for _, ks := range cs.keyStores {
|
||||
for k, r := range ks.ListKeys() {
|
||||
if r == role {
|
||||
res = append(res, k)
|
||||
|
|
@ -163,3 +163,14 @@ func (ccs *CryptoService) ListKeys(role string) []string {
|
|||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ListKeys returns a list of key IDs valid for the given role
|
||||
func (cs *CryptoService) ListAllKeys() map[string]string {
|
||||
res := make(map[string]string)
|
||||
for _, ks := range cs.keyStores {
|
||||
for k, r := range ks.ListKeys() {
|
||||
res[k] = r // keys are content addressed so don't care about overwrites
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package keystoremanager
|
||||
package cryptoservice
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
|
|
@ -11,7 +11,8 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/keystoremanager"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
)
|
||||
|
||||
|
|
@ -33,11 +34,22 @@ var (
|
|||
|
||||
// ExportRootKey exports the specified root key to an io.Writer in PEM format.
|
||||
// The key's existing encryption is preserved.
|
||||
func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error {
|
||||
pemBytes, err := km.KeyStore.ExportKey(keyID)
|
||||
func (cs *CryptoService) ExportRootKey(dest io.Writer, keyID string) error {
|
||||
var (
|
||||
pemBytes []byte
|
||||
err error
|
||||
)
|
||||
|
||||
for _, ks := range cs.keyStores {
|
||||
pemBytes, err = ks.ExportKey(keyID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nBytes, err := dest.Write(pemBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -50,8 +62,8 @@ func (km *KeyStoreManager) ExportRootKey(dest io.Writer, keyID string) error {
|
|||
|
||||
// ExportRootKeyReencrypt exports the specified root key to an io.Writer in
|
||||
// PEM format. The key is reencrypted with a new passphrase.
|
||||
func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error {
|
||||
privateKey, alias, err := km.KeyStore.GetKey(keyID)
|
||||
func (cs *CryptoService) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error {
|
||||
privateKey, role, err := cs.GetPrivateKey(keyID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -60,13 +72,13 @@ func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string,
|
|||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
tempKeysPath := filepath.Join(tempBaseDir, PrivDir)
|
||||
tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir)
|
||||
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tempKeyStore.AddKey(keyID, alias, privateKey)
|
||||
err = tempKeyStore.AddKey(keyID, role, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -85,25 +97,10 @@ func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string,
|
|||
return nil
|
||||
}
|
||||
|
||||
// checkRootKeyIsEncrypted makes sure the root key is encrypted. We have
|
||||
// internal assumptions that depend on this.
|
||||
func checkRootKeyIsEncrypted(pemBytes []byte) error {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return ErrNoValidPrivateKey
|
||||
}
|
||||
|
||||
if !x509.IsEncryptedPEMBlock(block) {
|
||||
return ErrRootKeyNotEncrypted
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportRootKey imports a root in PEM format key from an io.Reader
|
||||
// It prompts for the key's passphrase to verify the data and to determine
|
||||
// the key ID.
|
||||
func (km *KeyStoreManager) ImportRootKey(source io.Reader) error {
|
||||
func (cs *CryptoService) ImportRootKey(source io.Reader) error {
|
||||
pemBytes, err := ioutil.ReadAll(source)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -113,14 +110,158 @@ func (km *KeyStoreManager) ImportRootKey(source io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err = km.KeyStore.ImportKey(pemBytes, "root"); err != nil {
|
||||
return err
|
||||
for _, ks := range cs.keyStores {
|
||||
// don't redeclare err, we want the value carried out of the loop
|
||||
if err = ks.ImportKey(pemBytes, "root"); err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func moveKeys(oldKeyStore, newKeyStore *trustmanager.KeyFileStore) error {
|
||||
// ExportAllKeys exports all keys to an io.Writer in zip format.
|
||||
// newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys.
|
||||
func (cs *CryptoService) ExportAllKeys(dest io.Writer, newPassphraseRetriever passphrase.Retriever) error {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
// Create temporary keystore to use as a staging area
|
||||
tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir)
|
||||
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ks := range cs.keyStores {
|
||||
if err := moveKeys(ks, tempKeyStore); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zipWriter := zip.NewWriter(dest)
|
||||
|
||||
if err := addKeysToArchive(zipWriter, tempKeyStore, keystoremanager.PrivDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipWriter.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportKeysZip imports keys from a zip file provided as an zip.Reader. The
|
||||
// keys in the root_keys directory are left encrypted, but the other keys are
|
||||
// decrypted with the specified passphrase.
|
||||
func (cs *CryptoService) ImportKeysZip(zipReader zip.Reader) error {
|
||||
// Temporarily store the keys in maps, so we can bail early if there's
|
||||
// an error (for example, wrong passphrase), without leaving the key
|
||||
// store in an inconsistent state
|
||||
newKeys := make(map[string][]byte)
|
||||
|
||||
// Iterate through the files in the archive. Don't add the keys
|
||||
for _, f := range zipReader.File {
|
||||
fNameTrimmed := strings.TrimSuffix(f.Name, filepath.Ext(f.Name))
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
fileBytes, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that using / as a separator is okay here - the zip
|
||||
// package guarantees that the separator will be /
|
||||
if strings.HasPrefix(fNameTrimmed, keystoremanager.PrivDir) {
|
||||
if fNameTrimmed[len(fNameTrimmed)-5:] == "_root" {
|
||||
if err = checkRootKeyIsEncrypted(fileBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
keyName := strings.TrimPrefix(fNameTrimmed, keystoremanager.PrivDir)
|
||||
newKeys[keyName] = fileBytes
|
||||
} else {
|
||||
// This path inside the zip archive doesn't look like a
|
||||
// root key, non-root key, or alias. To avoid adding a file
|
||||
// to the filestore that we won't be able to use, skip
|
||||
// this file in the import.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for keyName, pemBytes := range newKeys {
|
||||
parts := strings.Split(keyName, "_")
|
||||
for _, ks := range cs.keyStores {
|
||||
if err := ks.ImportKey(pemBytes, parts[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExportKeysByGUN exports all keys associated with a specified GUN to an
|
||||
// io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to
|
||||
// encrypt the keys.
|
||||
func (cs *CryptoService) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
// Create temporary keystore to use as a staging area
|
||||
tempKeysPath := filepath.Join(tempBaseDir, keystoremanager.PrivDir)
|
||||
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ks := range cs.keyStores {
|
||||
if err := moveKeysByGUN(ks, tempKeyStore, gun); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
zipWriter := zip.NewWriter(dest)
|
||||
|
||||
if len(tempKeyStore.ListKeys()) == 0 {
|
||||
return ErrNoKeysFoundForGUN
|
||||
}
|
||||
|
||||
if err := addKeysToArchive(zipWriter, tempKeyStore, keystoremanager.PrivDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipWriter.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func moveKeysByGUN(oldKeyStore, newKeyStore trustmanager.KeyStore, gun string) error {
|
||||
for relKeyPath := range oldKeyStore.ListKeys() {
|
||||
// Skip keys that aren't associated with this GUN
|
||||
if !strings.HasPrefix(relKeyPath, filepath.FromSlash(gun)) {
|
||||
continue
|
||||
}
|
||||
|
||||
privKey, alias, err := oldKeyStore.GetKey(relKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = newKeyStore.AddKey(relKeyPath, alias, privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func moveKeys(oldKeyStore, newKeyStore trustmanager.KeyStore) error {
|
||||
for f := range oldKeyStore.ListKeys() {
|
||||
privateKey, alias, err := oldKeyStore.GetKey(f)
|
||||
if err != nil {
|
||||
|
|
@ -171,139 +312,17 @@ func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileSt
|
|||
return nil
|
||||
}
|
||||
|
||||
// ExportAllKeys exports all keys to an io.Writer in zip format.
|
||||
// newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys.
|
||||
func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever passphrase.Retriever) error {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
// Create temporary keystore to use as a staging area
|
||||
tempKeysPath := filepath.Join(tempBaseDir, PrivDir)
|
||||
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever)
|
||||
if err != nil {
|
||||
return err
|
||||
// checkRootKeyIsEncrypted makes sure the root key is encrypted. We have
|
||||
// internal assumptions that depend on this.
|
||||
func checkRootKeyIsEncrypted(pemBytes []byte) error {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return ErrNoValidPrivateKey
|
||||
}
|
||||
|
||||
if err := moveKeys(km.KeyStore, tempKeyStore); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipWriter := zip.NewWriter(dest)
|
||||
|
||||
if err := addKeysToArchive(zipWriter, tempKeyStore, PrivDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipWriter.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportKeysZip imports keys from a zip file provided as an zip.Reader. The
|
||||
// keys in the root_keys directory are left encrypted, but the other keys are
|
||||
// decrypted with the specified passphrase.
|
||||
func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader) error {
|
||||
// Temporarily store the keys in maps, so we can bail early if there's
|
||||
// an error (for example, wrong passphrase), without leaving the key
|
||||
// store in an inconsistent state
|
||||
newKeys := make(map[string][]byte)
|
||||
|
||||
// Iterate through the files in the archive. Don't add the keys
|
||||
for _, f := range zipReader.File {
|
||||
fNameTrimmed := strings.TrimSuffix(f.Name, filepath.Ext(f.Name))
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileBytes, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that using / as a separator is okay here - the zip
|
||||
// package guarantees that the separator will be /
|
||||
if strings.HasPrefix(fNameTrimmed, PrivDir) {
|
||||
if fNameTrimmed[len(fNameTrimmed)-5:] == "_root" {
|
||||
if err = checkRootKeyIsEncrypted(fileBytes); err != nil {
|
||||
rc.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
keyName := strings.TrimPrefix(fNameTrimmed, PrivDir)
|
||||
newKeys[keyName] = fileBytes
|
||||
} else {
|
||||
// This path inside the zip archive doesn't look like a
|
||||
// root key, non-root key, or alias. To avoid adding a file
|
||||
// to the filestore that we won't be able to use, skip
|
||||
// this file in the import.
|
||||
rc.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
rc.Close()
|
||||
}
|
||||
|
||||
for keyName, pemBytes := range newKeys {
|
||||
if err := km.KeyStore.Add(keyName, pemBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
if !x509.IsEncryptedPEMBlock(block) {
|
||||
return ErrRootKeyNotEncrypted
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun string) error {
|
||||
for relKeyPath := range oldKeyStore.ListKeys() {
|
||||
// Skip keys that aren't associated with this GUN
|
||||
if !strings.HasPrefix(relKeyPath, filepath.FromSlash(gun)) {
|
||||
continue
|
||||
}
|
||||
|
||||
privKey, alias, err := oldKeyStore.GetKey(relKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = newKeyStore.AddKey(relKeyPath, alias, privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExportKeysByGUN exports all keys associated with a specified GUN to an
|
||||
// io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to
|
||||
// encrypt the keys.
|
||||
func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
// Create temporary keystore to use as a staging area
|
||||
tempKeysPath := filepath.Join(tempBaseDir, PrivDir)
|
||||
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := moveKeysByGUN(km.KeyStore, tempKeyStore, gun); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipWriter := zip.NewWriter(dest)
|
||||
|
||||
if len(tempKeyStore.ListKeys()) == 0 {
|
||||
return ErrNoKeysFoundForGUN
|
||||
}
|
||||
|
||||
if err := addKeysToArchive(zipWriter, tempKeyStore, PrivDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipWriter.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,447 @@
|
|||
package cryptoservice
|
||||
|
||||
//import (
|
||||
// "archive/zip"
|
||||
// "bytes"
|
||||
// "fmt"
|
||||
// "io/ioutil"
|
||||
// "net/http"
|
||||
// "net/http/httptest"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
// "testing"
|
||||
//
|
||||
// "github.com/docker/notary/client"
|
||||
// "github.com/docker/notary/keystoremanager"
|
||||
// "github.com/docker/notary/trustmanager"
|
||||
// "github.com/docker/notary/tuf/data"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
//)
|
||||
//
|
||||
//const timestampECDSAKeyJSON = `
|
||||
//{"keytype":"ecdsa","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}`
|
||||
//
|
||||
//func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
|
||||
// mux := http.NewServeMux()
|
||||
// // TUF will request /v2/docker.com/notary/_trust/tuf/timestamp.key
|
||||
// // Return a canned timestamp.key
|
||||
// mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.key", func(w http.ResponseWriter, r *http.Request) {
|
||||
// // Also contains the private key, but for the purpose of this
|
||||
// // test, we don't care
|
||||
// fmt.Fprint(w, timestampECDSAKeyJSON)
|
||||
// })
|
||||
//
|
||||
// ts := httptest.NewServer(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) {
|
||||
// gun := "docker.com/notary"
|
||||
//
|
||||
// // Temporary directory where test files will be created
|
||||
// tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// ts, _ := createTestServer(t)
|
||||
// defer ts.Close()
|
||||
//
|
||||
// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo.Initialize(rootKeyID)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// tempZipFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
// tempZipFilePath := tempZipFile.Name()
|
||||
// defer os.Remove(tempZipFilePath)
|
||||
//
|
||||
// err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, newPassphraseRetriever)
|
||||
// tempZipFile.Close()
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// // Reopen the zip file for importing
|
||||
// zipReader, err := zip.OpenReader(tempZipFilePath)
|
||||
// assert.NoError(t, err, "could not open zip file")
|
||||
//
|
||||
// // Map of files to expect in the zip file, with the passphrases
|
||||
// passphraseByFile := make(map[string]string)
|
||||
//
|
||||
// // Add non-root keys to the map. These should use the new passphrase
|
||||
// // because the passwords were chosen by the newPassphraseRetriever.
|
||||
// privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys()
|
||||
// for privKeyName := range privKeyMap {
|
||||
// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
// assert.NoError(t, err, "privKey %s has no alias", privKeyName)
|
||||
//
|
||||
// if alias == "root" {
|
||||
// continue
|
||||
// }
|
||||
// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
// passphraseByFile[relKeyPath] = exportPassphrase
|
||||
// }
|
||||
//
|
||||
// // Add root key to the map. This will use the export passphrase because it
|
||||
// // will be reencrypted.
|
||||
// relRootKey := filepath.Join("private", "root_keys", rootKeyID+"_root.key")
|
||||
// passphraseByFile[relRootKey] = exportPassphrase
|
||||
//
|
||||
// // Iterate through the files in the archive, checking that the files
|
||||
// // exist and are encrypted with the expected passphrase.
|
||||
// for _, f := range zipReader.File {
|
||||
// expectedPassphrase, present := passphraseByFile[f.Name]
|
||||
// if !present {
|
||||
// t.Fatalf("unexpected file %s in zip file", f.Name)
|
||||
// }
|
||||
//
|
||||
// delete(passphraseByFile, f.Name)
|
||||
//
|
||||
// rc, err := f.Open()
|
||||
// assert.NoError(t, err, "could not open file inside zip archive")
|
||||
//
|
||||
// pemBytes, err := ioutil.ReadAll(rc)
|
||||
// assert.NoError(t, err, "could not read file from zip")
|
||||
//
|
||||
// _, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase)
|
||||
// assert.NoError(t, err, "PEM not encrypted with the expected passphrase")
|
||||
//
|
||||
// rc.Close()
|
||||
// }
|
||||
//
|
||||
// zipReader.Close()
|
||||
//
|
||||
// // Are there any keys that didn't make it to the zip?
|
||||
// for fileNotFound := range passphraseByFile {
|
||||
// t.Fatalf("%s not found in zip", fileNotFound)
|
||||
// }
|
||||
//
|
||||
// // Create new repo to test import
|
||||
// tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir2)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo2.Initialize(rootKeyID2)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// // Reopen the zip file for importing
|
||||
// zipReader, err = zip.OpenReader(tempZipFilePath)
|
||||
// assert.NoError(t, err, "could not open zip file")
|
||||
//
|
||||
// // Now try with a valid passphrase. This time it should succeed.
|
||||
// err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader)
|
||||
// assert.NoError(t, err)
|
||||
// zipReader.Close()
|
||||
//
|
||||
// // Look for keys in private. The filenames should match the key IDs
|
||||
// // in the repo's private key store.
|
||||
// for privKeyName := range privKeyMap {
|
||||
// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
// assert.NoError(t, err, "privKey %s has no alias", privKeyName)
|
||||
//
|
||||
// if alias == "root" {
|
||||
// continue
|
||||
// }
|
||||
// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
// privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
|
||||
// _, err = os.Stat(privKeyFileName)
|
||||
// assert.NoError(t, err, "missing private key: %s", privKeyName)
|
||||
// }
|
||||
//
|
||||
// // Look for keys in root_keys
|
||||
// // There should be a file named after the key ID of the root key we
|
||||
// // passed in.
|
||||
// rootKeyFilename := rootKeyID + "_root.key"
|
||||
// _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
|
||||
// assert.NoError(t, err, "missing root key")
|
||||
//}
|
||||
//
|
||||
//func TestImportExportGUN(t *testing.T) {
|
||||
// gun := "docker.com/notary"
|
||||
//
|
||||
// // Temporary directory where test files will be created
|
||||
// tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// ts, _ := createTestServer(t)
|
||||
// defer ts.Close()
|
||||
//
|
||||
// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo.Initialize(rootKeyID)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// tempZipFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
// tempZipFilePath := tempZipFile.Name()
|
||||
// defer os.Remove(tempZipFilePath)
|
||||
//
|
||||
// err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// // With an invalid GUN, this should return an error
|
||||
// err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", newPassphraseRetriever)
|
||||
// assert.EqualError(t, err, keystoremanager.ErrNoKeysFoundForGUN.Error())
|
||||
//
|
||||
// tempZipFile.Close()
|
||||
//
|
||||
// // Reopen the zip file for importing
|
||||
// zipReader, err := zip.OpenReader(tempZipFilePath)
|
||||
// assert.NoError(t, err, "could not open zip file")
|
||||
//
|
||||
// // Map of files to expect in the zip file, with the passphrases
|
||||
// passphraseByFile := make(map[string]string)
|
||||
//
|
||||
// // Add keys non-root keys to the map. These should use the new passphrase
|
||||
// // because they were formerly unencrypted.
|
||||
// privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys()
|
||||
// for privKeyName := range privKeyMap {
|
||||
// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
// if err != nil {
|
||||
// t.Fatalf("privKey %s has no alias", privKeyName)
|
||||
// }
|
||||
// if alias == "root" {
|
||||
// continue
|
||||
// }
|
||||
// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
//
|
||||
// passphraseByFile[relKeyPath] = exportPassphrase
|
||||
// }
|
||||
//
|
||||
// // Iterate through the files in the archive, checking that the files
|
||||
// // exist and are encrypted with the expected passphrase.
|
||||
// for _, f := range zipReader.File {
|
||||
//
|
||||
// expectedPassphrase, present := passphraseByFile[f.Name]
|
||||
// if !present {
|
||||
// t.Fatalf("unexpected file %s in zip file", f.Name)
|
||||
// }
|
||||
//
|
||||
// delete(passphraseByFile, f.Name)
|
||||
//
|
||||
// rc, err := f.Open()
|
||||
// assert.NoError(t, err, "could not open file inside zip archive")
|
||||
//
|
||||
// pemBytes, err := ioutil.ReadAll(rc)
|
||||
// assert.NoError(t, err, "could not read file from zip")
|
||||
//
|
||||
// _, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase)
|
||||
// assert.NoError(t, err, "PEM not encrypted with the expected passphrase")
|
||||
//
|
||||
// rc.Close()
|
||||
// }
|
||||
//
|
||||
// zipReader.Close()
|
||||
//
|
||||
// // Are there any keys that didn't make it to the zip?
|
||||
// for fileNotFound := range passphraseByFile {
|
||||
// t.Fatalf("%s not found in zip", fileNotFound)
|
||||
// }
|
||||
//
|
||||
// // Create new repo to test import
|
||||
// tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir2)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo2.Initialize(rootKeyID2)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// // Reopen the zip file for importing
|
||||
// zipReader, err = zip.OpenReader(tempZipFilePath)
|
||||
// assert.NoError(t, err, "could not open zip file")
|
||||
//
|
||||
// // Now try with a valid passphrase. This time it should succeed.
|
||||
// err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader)
|
||||
// assert.NoError(t, err)
|
||||
// zipReader.Close()
|
||||
//
|
||||
// // Look for keys in private. The filenames should match the key IDs
|
||||
// // in the repo's private key store.
|
||||
// for privKeyName := range privKeyMap {
|
||||
// _, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
// if err != nil {
|
||||
// t.Fatalf("privKey %s has no alias", privKeyName)
|
||||
// }
|
||||
// if alias == "root" {
|
||||
// continue
|
||||
// }
|
||||
// relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
// privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
|
||||
// _, err = os.Stat(privKeyFileName)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImportExportRootKey(t *testing.T) {
|
||||
// gun := "docker.com/notary"
|
||||
//
|
||||
// // Temporary directory where test files will be created
|
||||
// tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// ts, _ := createTestServer(t)
|
||||
// defer ts.Close()
|
||||
//
|
||||
// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo.Initialize(rootKeyID)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// tempKeyFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
// tempKeyFilePath := tempKeyFile.Name()
|
||||
// defer os.Remove(tempKeyFilePath)
|
||||
//
|
||||
// err = repo.KeyStoreManager.ExportRootKey(tempKeyFile, rootKeyID)
|
||||
// assert.NoError(t, err)
|
||||
// tempKeyFile.Close()
|
||||
//
|
||||
// // Create new repo to test import
|
||||
// tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir2)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo2.Initialize(rootKeyID2)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// keyReader, err := os.Open(tempKeyFilePath)
|
||||
// assert.NoError(t, err, "could not open key file")
|
||||
//
|
||||
// err = repo2.KeyStoreManager.ImportRootKey(keyReader)
|
||||
// assert.NoError(t, err)
|
||||
// keyReader.Close()
|
||||
//
|
||||
// // Look for repo's root key in repo2
|
||||
// // There should be a file named after the key ID of the root key we
|
||||
// // imported.
|
||||
// rootKeyFilename := rootKeyID + "_root.key"
|
||||
// _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
|
||||
// assert.NoError(t, err, "missing root key")
|
||||
//
|
||||
// // Try to import a decrypted version of the root key and make sure it
|
||||
// // doesn't succeed
|
||||
// pemBytes, err := ioutil.ReadFile(tempKeyFilePath)
|
||||
// assert.NoError(t, err, "could not read key file")
|
||||
// privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, oldPassphrase)
|
||||
// assert.NoError(t, err, "could not decrypt key file")
|
||||
// decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey)
|
||||
// assert.NoError(t, err, "could not convert key to PEM")
|
||||
//
|
||||
// err = repo2.KeyStoreManager.ImportRootKey(bytes.NewReader(decryptedPEMBytes))
|
||||
// assert.EqualError(t, err, keystoremanager.ErrRootKeyNotEncrypted.Error())
|
||||
//
|
||||
// // Try to import garbage and make sure it doesn't succeed
|
||||
// err = repo2.KeyStoreManager.ImportRootKey(strings.NewReader("this is not PEM"))
|
||||
// assert.EqualError(t, err, keystoremanager.ErrNoValidPrivateKey.Error())
|
||||
//
|
||||
// // Should be able to unlock the root key with the old password
|
||||
// key, alias, err := repo2.KeyStoreManager.KeyStore.GetKey(rootKeyID)
|
||||
// assert.NoError(t, err, "could not unlock root key")
|
||||
// assert.Equal(t, "root", alias)
|
||||
// assert.Equal(t, rootKeyID, key.ID())
|
||||
//}
|
||||
//
|
||||
//func TestImportExportRootKeyReencrypt(t *testing.T) {
|
||||
// gun := "docker.com/notary"
|
||||
//
|
||||
// // Temporary directory where test files will be created
|
||||
// tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// ts, _ := createTestServer(t)
|
||||
// defer ts.Close()
|
||||
//
|
||||
// repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo.Initialize(rootKeyID)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// tempKeyFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
// tempKeyFilePath := tempKeyFile.Name()
|
||||
// defer os.Remove(tempKeyFilePath)
|
||||
//
|
||||
// err = repo.KeyStoreManager.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever)
|
||||
// assert.NoError(t, err)
|
||||
// tempKeyFile.Close()
|
||||
//
|
||||
// // Create new repo to test import
|
||||
// tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
// defer os.RemoveAll(tempBaseDir2)
|
||||
//
|
||||
// assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
//
|
||||
// repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever)
|
||||
// assert.NoError(t, err, "error creating repo: %s", err)
|
||||
//
|
||||
// rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
// assert.NoError(t, err, "error generating root key: %s", err)
|
||||
//
|
||||
// err = repo2.Initialize(rootKeyID2)
|
||||
// assert.NoError(t, err, "error creating repository: %s", err)
|
||||
//
|
||||
// keyReader, err := os.Open(tempKeyFilePath)
|
||||
// assert.NoError(t, err, "could not open key file")
|
||||
//
|
||||
// err = repo2.KeyStoreManager.ImportRootKey(keyReader)
|
||||
// assert.NoError(t, err)
|
||||
// keyReader.Close()
|
||||
//
|
||||
// // Look for repo's root key in repo2
|
||||
// // There should be a file named after the key ID of the root key we
|
||||
// // imported.
|
||||
// rootKeyFilename := rootKeyID + "_root.key"
|
||||
// _, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
|
||||
// assert.NoError(t, err, "missing root key")
|
||||
//
|
||||
// // Should be able to unlock the root key with the new password
|
||||
// key, alias, err := repo2.KeyStoreManager.KeyStore.GetKey(rootKeyID)
|
||||
// assert.NoError(t, err, "could not unlock root key")
|
||||
// assert.Equal(t, "root", alias)
|
||||
// assert.Equal(t, rootKeyID, key.ID())
|
||||
//}
|
||||
|
|
@ -1,447 +0,0 @@
|
|||
package keystoremanager_test
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/notary/client"
|
||||
"github.com/docker/notary/keystoremanager"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const timestampECDSAKeyJSON = `
|
||||
{"keytype":"ecdsa","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}`
|
||||
|
||||
func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
|
||||
mux := http.NewServeMux()
|
||||
// TUF will request /v2/docker.com/notary/_trust/tuf/timestamp.key
|
||||
// Return a canned timestamp.key
|
||||
mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.key", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Also contains the private key, but for the purpose of this
|
||||
// test, we don't care
|
||||
fmt.Fprint(w, timestampECDSAKeyJSON)
|
||||
})
|
||||
|
||||
ts := httptest.NewServer(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) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
ts, _ := createTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo.Initialize(rootKeyID)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
tempZipFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
tempZipFilePath := tempZipFile.Name()
|
||||
defer os.Remove(tempZipFilePath)
|
||||
|
||||
err = repo.KeyStoreManager.ExportAllKeys(tempZipFile, newPassphraseRetriever)
|
||||
tempZipFile.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Reopen the zip file for importing
|
||||
zipReader, err := zip.OpenReader(tempZipFilePath)
|
||||
assert.NoError(t, err, "could not open zip file")
|
||||
|
||||
// Map of files to expect in the zip file, with the passphrases
|
||||
passphraseByFile := make(map[string]string)
|
||||
|
||||
// Add non-root keys to the map. These should use the new passphrase
|
||||
// because the passwords were chosen by the newPassphraseRetriever.
|
||||
privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys()
|
||||
for privKeyName := range privKeyMap {
|
||||
_, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
assert.NoError(t, err, "privKey %s has no alias", privKeyName)
|
||||
|
||||
if alias == "root" {
|
||||
continue
|
||||
}
|
||||
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
passphraseByFile[relKeyPath] = exportPassphrase
|
||||
}
|
||||
|
||||
// Add root key to the map. This will use the export passphrase because it
|
||||
// will be reencrypted.
|
||||
relRootKey := filepath.Join("private", "root_keys", rootKeyID+"_root.key")
|
||||
passphraseByFile[relRootKey] = exportPassphrase
|
||||
|
||||
// Iterate through the files in the archive, checking that the files
|
||||
// exist and are encrypted with the expected passphrase.
|
||||
for _, f := range zipReader.File {
|
||||
expectedPassphrase, present := passphraseByFile[f.Name]
|
||||
if !present {
|
||||
t.Fatalf("unexpected file %s in zip file", f.Name)
|
||||
}
|
||||
|
||||
delete(passphraseByFile, f.Name)
|
||||
|
||||
rc, err := f.Open()
|
||||
assert.NoError(t, err, "could not open file inside zip archive")
|
||||
|
||||
pemBytes, err := ioutil.ReadAll(rc)
|
||||
assert.NoError(t, err, "could not read file from zip")
|
||||
|
||||
_, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase)
|
||||
assert.NoError(t, err, "PEM not encrypted with the expected passphrase")
|
||||
|
||||
rc.Close()
|
||||
}
|
||||
|
||||
zipReader.Close()
|
||||
|
||||
// Are there any keys that didn't make it to the zip?
|
||||
for fileNotFound := range passphraseByFile {
|
||||
t.Fatalf("%s not found in zip", fileNotFound)
|
||||
}
|
||||
|
||||
// Create new repo to test import
|
||||
tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir2)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo2.Initialize(rootKeyID2)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
// Reopen the zip file for importing
|
||||
zipReader, err = zip.OpenReader(tempZipFilePath)
|
||||
assert.NoError(t, err, "could not open zip file")
|
||||
|
||||
// Now try with a valid passphrase. This time it should succeed.
|
||||
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader)
|
||||
assert.NoError(t, err)
|
||||
zipReader.Close()
|
||||
|
||||
// Look for keys in private. The filenames should match the key IDs
|
||||
// in the repo's private key store.
|
||||
for privKeyName := range privKeyMap {
|
||||
_, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
assert.NoError(t, err, "privKey %s has no alias", privKeyName)
|
||||
|
||||
if alias == "root" {
|
||||
continue
|
||||
}
|
||||
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
|
||||
_, err = os.Stat(privKeyFileName)
|
||||
assert.NoError(t, err, "missing private key: %s", privKeyName)
|
||||
}
|
||||
|
||||
// Look for keys in root_keys
|
||||
// There should be a file named after the key ID of the root key we
|
||||
// passed in.
|
||||
rootKeyFilename := rootKeyID + "_root.key"
|
||||
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
|
||||
assert.NoError(t, err, "missing root key")
|
||||
}
|
||||
|
||||
func TestImportExportGUN(t *testing.T) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
ts, _ := createTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo.Initialize(rootKeyID)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
tempZipFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
tempZipFilePath := tempZipFile.Name()
|
||||
defer os.Remove(tempZipFilePath)
|
||||
|
||||
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, gun, newPassphraseRetriever)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// With an invalid GUN, this should return an error
|
||||
err = repo.KeyStoreManager.ExportKeysByGUN(tempZipFile, "does.not.exist/in/repository", newPassphraseRetriever)
|
||||
assert.EqualError(t, err, keystoremanager.ErrNoKeysFoundForGUN.Error())
|
||||
|
||||
tempZipFile.Close()
|
||||
|
||||
// Reopen the zip file for importing
|
||||
zipReader, err := zip.OpenReader(tempZipFilePath)
|
||||
assert.NoError(t, err, "could not open zip file")
|
||||
|
||||
// Map of files to expect in the zip file, with the passphrases
|
||||
passphraseByFile := make(map[string]string)
|
||||
|
||||
// Add keys non-root keys to the map. These should use the new passphrase
|
||||
// because they were formerly unencrypted.
|
||||
privKeyMap := repo.KeyStoreManager.KeyStore.ListKeys()
|
||||
for privKeyName := range privKeyMap {
|
||||
_, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
if err != nil {
|
||||
t.Fatalf("privKey %s has no alias", privKeyName)
|
||||
}
|
||||
if alias == "root" {
|
||||
continue
|
||||
}
|
||||
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
|
||||
passphraseByFile[relKeyPath] = exportPassphrase
|
||||
}
|
||||
|
||||
// Iterate through the files in the archive, checking that the files
|
||||
// exist and are encrypted with the expected passphrase.
|
||||
for _, f := range zipReader.File {
|
||||
|
||||
expectedPassphrase, present := passphraseByFile[f.Name]
|
||||
if !present {
|
||||
t.Fatalf("unexpected file %s in zip file", f.Name)
|
||||
}
|
||||
|
||||
delete(passphraseByFile, f.Name)
|
||||
|
||||
rc, err := f.Open()
|
||||
assert.NoError(t, err, "could not open file inside zip archive")
|
||||
|
||||
pemBytes, err := ioutil.ReadAll(rc)
|
||||
assert.NoError(t, err, "could not read file from zip")
|
||||
|
||||
_, err = trustmanager.ParsePEMPrivateKey(pemBytes, expectedPassphrase)
|
||||
assert.NoError(t, err, "PEM not encrypted with the expected passphrase")
|
||||
|
||||
rc.Close()
|
||||
}
|
||||
|
||||
zipReader.Close()
|
||||
|
||||
// Are there any keys that didn't make it to the zip?
|
||||
for fileNotFound := range passphraseByFile {
|
||||
t.Fatalf("%s not found in zip", fileNotFound)
|
||||
}
|
||||
|
||||
// Create new repo to test import
|
||||
tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir2)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo2.Initialize(rootKeyID2)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
// Reopen the zip file for importing
|
||||
zipReader, err = zip.OpenReader(tempZipFilePath)
|
||||
assert.NoError(t, err, "could not open zip file")
|
||||
|
||||
// Now try with a valid passphrase. This time it should succeed.
|
||||
err = repo2.KeyStoreManager.ImportKeysZip(zipReader.Reader)
|
||||
assert.NoError(t, err)
|
||||
zipReader.Close()
|
||||
|
||||
// Look for keys in private. The filenames should match the key IDs
|
||||
// in the repo's private key store.
|
||||
for privKeyName := range privKeyMap {
|
||||
_, alias, err := repo.KeyStoreManager.KeyStore.GetKey(privKeyName)
|
||||
if err != nil {
|
||||
t.Fatalf("privKey %s has no alias", privKeyName)
|
||||
}
|
||||
if alias == "root" {
|
||||
continue
|
||||
}
|
||||
relKeyPath := filepath.Join("private", "tuf_keys", privKeyName+"_"+alias+".key")
|
||||
privKeyFileName := filepath.Join(tempBaseDir2, relKeyPath)
|
||||
_, err = os.Stat(privKeyFileName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportExportRootKey(t *testing.T) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
ts, _ := createTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo.Initialize(rootKeyID)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
tempKeyFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
tempKeyFilePath := tempKeyFile.Name()
|
||||
defer os.Remove(tempKeyFilePath)
|
||||
|
||||
err = repo.KeyStoreManager.ExportRootKey(tempKeyFile, rootKeyID)
|
||||
assert.NoError(t, err)
|
||||
tempKeyFile.Close()
|
||||
|
||||
// Create new repo to test import
|
||||
tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir2)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo2.Initialize(rootKeyID2)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
keyReader, err := os.Open(tempKeyFilePath)
|
||||
assert.NoError(t, err, "could not open key file")
|
||||
|
||||
err = repo2.KeyStoreManager.ImportRootKey(keyReader)
|
||||
assert.NoError(t, err)
|
||||
keyReader.Close()
|
||||
|
||||
// Look for repo's root key in repo2
|
||||
// There should be a file named after the key ID of the root key we
|
||||
// imported.
|
||||
rootKeyFilename := rootKeyID + "_root.key"
|
||||
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
|
||||
assert.NoError(t, err, "missing root key")
|
||||
|
||||
// Try to import a decrypted version of the root key and make sure it
|
||||
// doesn't succeed
|
||||
pemBytes, err := ioutil.ReadFile(tempKeyFilePath)
|
||||
assert.NoError(t, err, "could not read key file")
|
||||
privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, oldPassphrase)
|
||||
assert.NoError(t, err, "could not decrypt key file")
|
||||
decryptedPEMBytes, err := trustmanager.KeyToPEM(privKey)
|
||||
assert.NoError(t, err, "could not convert key to PEM")
|
||||
|
||||
err = repo2.KeyStoreManager.ImportRootKey(bytes.NewReader(decryptedPEMBytes))
|
||||
assert.EqualError(t, err, keystoremanager.ErrRootKeyNotEncrypted.Error())
|
||||
|
||||
// Try to import garbage and make sure it doesn't succeed
|
||||
err = repo2.KeyStoreManager.ImportRootKey(strings.NewReader("this is not PEM"))
|
||||
assert.EqualError(t, err, keystoremanager.ErrNoValidPrivateKey.Error())
|
||||
|
||||
// Should be able to unlock the root key with the old password
|
||||
key, alias, err := repo2.KeyStoreManager.KeyStore.GetKey(rootKeyID)
|
||||
assert.NoError(t, err, "could not unlock root key")
|
||||
assert.Equal(t, "root", alias)
|
||||
assert.Equal(t, rootKeyID, key.ID())
|
||||
}
|
||||
|
||||
func TestImportExportRootKeyReencrypt(t *testing.T) {
|
||||
gun := "docker.com/notary"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
ts, _ := createTestServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
repo, err := client.NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, oldPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo.Initialize(rootKeyID)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
tempKeyFile, err := ioutil.TempFile("", "notary-test-export-")
|
||||
tempKeyFilePath := tempKeyFile.Name()
|
||||
defer os.Remove(tempKeyFilePath)
|
||||
|
||||
err = repo.KeyStoreManager.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever)
|
||||
assert.NoError(t, err)
|
||||
tempKeyFile.Close()
|
||||
|
||||
// Create new repo to test import
|
||||
tempBaseDir2, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir2)
|
||||
|
||||
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
|
||||
|
||||
repo2, err := client.NewNotaryRepository(tempBaseDir2, gun, ts.URL, http.DefaultTransport, newPassphraseRetriever)
|
||||
assert.NoError(t, err, "error creating repo: %s", err)
|
||||
|
||||
rootKeyID2, err := repo2.KeyStoreManager.GenRootKey(data.ECDSAKey)
|
||||
assert.NoError(t, err, "error generating root key: %s", err)
|
||||
|
||||
err = repo2.Initialize(rootKeyID2)
|
||||
assert.NoError(t, err, "error creating repository: %s", err)
|
||||
|
||||
keyReader, err := os.Open(tempKeyFilePath)
|
||||
assert.NoError(t, err, "could not open key file")
|
||||
|
||||
err = repo2.KeyStoreManager.ImportRootKey(keyReader)
|
||||
assert.NoError(t, err)
|
||||
keyReader.Close()
|
||||
|
||||
// Look for repo's root key in repo2
|
||||
// There should be a file named after the key ID of the root key we
|
||||
// imported.
|
||||
rootKeyFilename := rootKeyID + "_root.key"
|
||||
_, err = os.Stat(filepath.Join(tempBaseDir2, "private", "root_keys", rootKeyFilename))
|
||||
assert.NoError(t, err, "missing root key")
|
||||
|
||||
// Should be able to unlock the root key with the new password
|
||||
key, alias, err := repo2.KeyStoreManager.KeyStore.GetKey(rootKeyID)
|
||||
assert.NoError(t, err, "could not unlock root key")
|
||||
assert.Equal(t, "root", alias)
|
||||
assert.Equal(t, rootKeyID, key.ID())
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -113,22 +113,23 @@ func PromptRetrieverWithInOut(in io.Reader, out io.Writer, aliasMap map[string]s
|
|||
indexOfLastSeparator = 0
|
||||
}
|
||||
|
||||
var shortName string
|
||||
if len(keyName) > indexOfLastSeparator+idBytesToDisplay {
|
||||
if indexOfLastSeparator > 0 {
|
||||
keyNamePrefix := keyName[:indexOfLastSeparator]
|
||||
keyNameID := keyName[indexOfLastSeparator+1 : indexOfLastSeparator+idBytesToDisplay+1]
|
||||
keyName = keyNamePrefix + " (" + keyNameID + ")"
|
||||
shortName = keyNamePrefix + " (" + keyNameID + ")"
|
||||
} else {
|
||||
keyName = keyName[indexOfLastSeparator : indexOfLastSeparator+idBytesToDisplay]
|
||||
shortName = keyName[indexOfLastSeparator : indexOfLastSeparator+idBytesToDisplay]
|
||||
}
|
||||
}
|
||||
|
||||
if createNew {
|
||||
fmt.Fprintf(out, "Enter passphrase for new %s key with id %s: ", displayAlias, keyName)
|
||||
fmt.Fprintf(out, "Enter passphrase for new %s key with id %s: ", displayAlias, shortName)
|
||||
} else if displayAlias == "yubikey" {
|
||||
fmt.Fprintf(out, "Enter the %s for the attached Yubikey: ", keyName)
|
||||
} else {
|
||||
fmt.Fprintf(out, "Enter passphrase for %s key with id %s: ", displayAlias, keyName)
|
||||
fmt.Fprintf(out, "Enter passphrase for %s key with id %s: ", displayAlias, shortName)
|
||||
}
|
||||
|
||||
passphrase, err := stdin.ReadBytes('\n')
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/docker/notary/signer/api"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/docker/notary/tuf/signed"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
pb "github.com/docker/notary/proto"
|
||||
|
|
@ -131,7 +132,7 @@ func TestHSMCreateKeyHandler(t *testing.T) {
|
|||
defer ctx.CloseSession(session)
|
||||
defer ctx.Logout(session)
|
||||
|
||||
cryptoService := api.NewRSAHardwareCryptoService(ctx, session)
|
||||
cryptoService := signed.NewEd25519()
|
||||
setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService})
|
||||
|
||||
createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.RSAKey)
|
||||
|
|
@ -182,7 +183,7 @@ func TestHSMSignHandler(t *testing.T) {
|
|||
defer ctx.CloseSession(session)
|
||||
defer ctx.Logout(session)
|
||||
|
||||
cryptoService := api.NewRSAHardwareCryptoService(ctx, session)
|
||||
cryptoService := signed.NewEd25519()
|
||||
setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService})
|
||||
|
||||
tufKey, _ := cryptoService.Create("", data.RSAKey)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/miekg/pkcs11"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/signer"
|
||||
"github.com/docker/notary/signer/api"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
|
|
|
|||
|
|
@ -1,212 +0,0 @@
|
|||
// +build pkcs11
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"github.com/docker/notary/signer/keys"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// RSAHardwareCryptoService is an implementation of SigningService
|
||||
type RSAHardwareCryptoService struct {
|
||||
keys map[string]*keys.HSMRSAKey
|
||||
context *pkcs11.Ctx
|
||||
session pkcs11.SessionHandle
|
||||
}
|
||||
|
||||
// ListKeys not implemented yet
|
||||
func (s *RSAHardwareCryptoService) ListKeys(role string) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Create creates a key and returns its public components
|
||||
func (s *RSAHardwareCryptoService) Create(role, algo string) (data.PublicKey, error) {
|
||||
// For now generate random labels for keys
|
||||
// (diogo): add link between keyID and label in database so we can support multiple keys
|
||||
randomLabel := make([]byte, 32)
|
||||
_, err := rand.Read(randomLabel)
|
||||
if err != nil {
|
||||
return nil, errors.New("Could not generate a random key label.")
|
||||
}
|
||||
|
||||
// Set the public key template
|
||||
// CKA_TOKEN: Guarantees key persistence in hardware
|
||||
// CKA_LABEL: Identifies this specific key inside of the HSM
|
||||
publicKeyTemplate := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{3}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, 2048),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)),
|
||||
}
|
||||
privateKeyTemplate := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, string(randomLabel)),
|
||||
}
|
||||
|
||||
// Generate a new RSA private/public keypair inside of the HSM
|
||||
pub, priv, err := s.context.GenerateKeyPair(s.session,
|
||||
[]*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
|
||||
publicKeyTemplate, privateKeyTemplate)
|
||||
if err != nil {
|
||||
return nil, errors.New("Could not generate a new key inside of the HSM.")
|
||||
}
|
||||
|
||||
// (diogo): This template is used for the GetAttribute
|
||||
template := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
|
||||
}
|
||||
|
||||
// Retrieve the public-key material to be able to create a new HSMRSAKey
|
||||
attr, err := s.context.GetAttributeValue(s.session, pub, template)
|
||||
if err != nil {
|
||||
return nil, errors.New("Failed to get Attribute value.")
|
||||
}
|
||||
|
||||
// We're going to store the elements of the RSA Public key, exponent and Modulus inside of exp and mod
|
||||
var exp int
|
||||
mod := big.NewInt(0)
|
||||
|
||||
// Iterate through all the attributes of this key and saves CKA_PUBLIC_EXPONENT and CKA_MODULUS. Removes ordering specific issues.
|
||||
for _, a := range attr {
|
||||
if a.Type == pkcs11.CKA_PUBLIC_EXPONENT {
|
||||
exp, _ = readInt(a.Value)
|
||||
}
|
||||
|
||||
if a.Type == pkcs11.CKA_MODULUS {
|
||||
mod.SetBytes(a.Value)
|
||||
}
|
||||
}
|
||||
|
||||
rsaPublicKey := rsa.PublicKey{N: mod, E: exp}
|
||||
// Using x509 to Marshal the Public key into der encoding
|
||||
pubBytes, err := x509.MarshalPKIXPublicKey(&rsaPublicKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("Failed to Marshal public key.")
|
||||
}
|
||||
|
||||
// (diogo): Ideally I would like to return base64 PEM encoded public keys to the client
|
||||
k := keys.NewHSMRSAKey(pubBytes, priv)
|
||||
|
||||
keyID := k.ID()
|
||||
|
||||
s.keys[keyID] = k
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// RemoveKey removes a key from the key database
|
||||
func (s *RSAHardwareCryptoService) RemoveKey(keyID string) error {
|
||||
if _, ok := s.keys[keyID]; !ok {
|
||||
return keys.ErrInvalidKeyID
|
||||
}
|
||||
|
||||
delete(s.keys, keyID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetKey returns the public components of a particular key
|
||||
func (s *RSAHardwareCryptoService) GetKey(keyID string) data.PublicKey {
|
||||
key, ok := s.keys[keyID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// GetPrivateKey is not implemented
|
||||
func (s *RSAHardwareCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
|
||||
return nil, "", errors.New("Not yet implemented")
|
||||
}
|
||||
|
||||
// Sign returns a signature for a given signature request
|
||||
func (s *RSAHardwareCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
|
||||
signatures := make([]data.Signature, 0, len(keyIDs))
|
||||
for _, keyid := range keyIDs {
|
||||
privateKey, present := s.keys[keyid]
|
||||
if !present {
|
||||
// We skip keys that aren't found
|
||||
continue
|
||||
}
|
||||
|
||||
priv := privateKey.PKCS11ObjectHandle()
|
||||
var sig []byte
|
||||
var err error
|
||||
for i := 0; i < 3; i++ {
|
||||
s.context.SignInit(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA256_RSA_PKCS, nil)}, priv)
|
||||
|
||||
sig, err = s.context.Sign(s.session, payload)
|
||||
if err != nil {
|
||||
log.Printf("Error while signing: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
digest := sha256.Sum256(payload)
|
||||
pub, err := x509.ParsePKIXPublicKey(privateKey.Public())
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse public key: %s\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rsaPub, ok := pub.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
log.Printf("Value returned from ParsePKIXPublicKey was not an RSA public key")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig)
|
||||
if err != nil {
|
||||
log.Printf("Failed verification. Retrying: %s", err)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if sig == nil {
|
||||
return nil, errors.New("Failed to create signature")
|
||||
}
|
||||
|
||||
signatures = append(signatures, data.Signature{
|
||||
KeyID: keyid,
|
||||
Method: data.RSAPKCS1v15Signature,
|
||||
Signature: sig[:],
|
||||
})
|
||||
}
|
||||
|
||||
return signatures, nil
|
||||
}
|
||||
|
||||
// NewRSAHardwareCryptoService returns an instance of RSAHardwareCryptoService
|
||||
func NewRSAHardwareCryptoService(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) *RSAHardwareCryptoService {
|
||||
return &RSAHardwareCryptoService{
|
||||
keys: make(map[string]*keys.HSMRSAKey),
|
||||
context: ctx,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
// readInt converts a []byte into an int. It is used to convert the RSA Public key exponent into an int to create a crypto.PublicKey
|
||||
func readInt(data []byte) (int, error) {
|
||||
var ret int
|
||||
if len(data) > 4 {
|
||||
return 0, errors.New("Cannot convert byte array due to size")
|
||||
}
|
||||
|
||||
for i, a := range data {
|
||||
ret |= (int(a) << uint(i*8))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
jose "github.com/dvsekhvalnov/jose2go"
|
||||
|
|
|
|||
|
|
@ -105,6 +105,11 @@ func (trust *NotarySigner) ListKeys(role string) []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
// ListAllKeys not supported for NotarySigner
|
||||
func (trust *NotarySigner) ListAllKeys() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
// CheckHealth checks the health of one of the clients, since both clients run
|
||||
// from the same GRPC server.
|
||||
func (trust *NotarySigner) CheckHealth(timeout time.Duration) error {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,22 +8,30 @@ import (
|
|||
"github.com/docker/notary/tuf/data"
|
||||
)
|
||||
|
||||
type edCryptoKey struct {
|
||||
role string
|
||||
privKey data.PrivateKey
|
||||
}
|
||||
|
||||
// Ed25519 implements a simple in memory cryptosystem for ED25519 keys
|
||||
type Ed25519 struct {
|
||||
keys map[string]data.PrivateKey
|
||||
keys map[string]edCryptoKey
|
||||
}
|
||||
|
||||
// NewEd25519 initializes a new empty Ed25519 CryptoService that operates
|
||||
// entirely in memory
|
||||
func NewEd25519() *Ed25519 {
|
||||
return &Ed25519{
|
||||
make(map[string]data.PrivateKey),
|
||||
make(map[string]edCryptoKey),
|
||||
}
|
||||
}
|
||||
|
||||
// addKey allows you to add a private key
|
||||
func (e *Ed25519) addKey(k data.PrivateKey) {
|
||||
e.keys[k.ID()] = k
|
||||
func (e *Ed25519) addKey(role string, k data.PrivateKey) {
|
||||
e.keys[k.ID()] = edCryptoKey{
|
||||
role: role,
|
||||
privKey: k,
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveKey deletes a key from the signer
|
||||
|
|
@ -41,12 +49,21 @@ func (e *Ed25519) ListKeys(role string) []string {
|
|||
return keyIDs
|
||||
}
|
||||
|
||||
// ListKeys returns the list of keys IDs for the role
|
||||
func (e *Ed25519) ListAllKeys() map[string]string {
|
||||
keys := make(map[string]string)
|
||||
for id, edKey := range e.keys {
|
||||
keys[id] = edKey.role
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Sign generates an Ed25519 signature over the data
|
||||
func (e *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
|
||||
signatures := make([]data.Signature, 0, len(keyIDs))
|
||||
for _, keyID := range keyIDs {
|
||||
priv := [ed25519.PrivateKeySize]byte{}
|
||||
copy(priv[:], e.keys[keyID].Private())
|
||||
copy(priv[:], e.keys[keyID].privKey.Private())
|
||||
sig := ed25519.Sign(&priv, toSign)
|
||||
signatures = append(signatures, data.Signature{
|
||||
KeyID: keyID,
|
||||
|
|
@ -74,7 +91,7 @@ func (e *Ed25519) Create(role, algorithm string) (data.PublicKey, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
e.addKey(private)
|
||||
e.addKey(role, private)
|
||||
return public, nil
|
||||
}
|
||||
|
||||
|
|
@ -83,8 +100,8 @@ func (e *Ed25519) Create(role, algorithm string) (data.PublicKey, error) {
|
|||
func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]data.PublicKey, error) {
|
||||
k := make(map[string]data.PublicKey)
|
||||
for _, keyID := range keyIDs {
|
||||
if key, ok := e.keys[keyID]; ok {
|
||||
k[keyID] = data.PublicKeyFromPrivate(key)
|
||||
if edKey, ok := e.keys[keyID]; ok {
|
||||
k[keyID] = data.PublicKeyFromPrivate(edKey.privKey)
|
||||
}
|
||||
}
|
||||
return k, nil
|
||||
|
|
@ -92,10 +109,10 @@ func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]data.PublicKey, error
|
|||
|
||||
// GetKey returns a single public key based on the ID
|
||||
func (e *Ed25519) GetKey(keyID string) data.PublicKey {
|
||||
return data.PublicKeyFromPrivate(e.keys[keyID])
|
||||
return data.PublicKeyFromPrivate(e.keys[keyID].privKey)
|
||||
}
|
||||
|
||||
// GetPrivateKey returns a single private key based on the ID
|
||||
func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
|
||||
return e.keys[keyID], "", nil
|
||||
return e.keys[keyID].privKey, "", nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,11 @@ type KeyService interface {
|
|||
// RemoveKey deletes the specified key
|
||||
RemoveKey(keyID string) error
|
||||
|
||||
// ListKeys returns a map of IDs to role
|
||||
// ListKeys returns a list of key IDs for the role
|
||||
ListKeys(role string) []string
|
||||
|
||||
// ListAllKeys returns a map of all available signing key IDs to role
|
||||
ListAllKeys() map[string]string
|
||||
}
|
||||
|
||||
// CryptoService defines a unified Signing and Key Service as this
|
||||
|
|
|
|||
|
|
@ -38,6 +38,15 @@ func (mts *FailingCryptoService) ListKeys(role string) []string {
|
|||
return []string{mts.testKey.ID()}
|
||||
}
|
||||
|
||||
func (mts *FailingCryptoService) ListAllKeys() map[string]string {
|
||||
return map[string]string{
|
||||
mts.testKey.ID(): "root",
|
||||
mts.testKey.ID(): "targets",
|
||||
mts.testKey.ID(): "snapshot",
|
||||
mts.testKey.ID(): "timestamp",
|
||||
}
|
||||
}
|
||||
|
||||
func (mts *FailingCryptoService) GetKey(keyID string) data.PublicKey {
|
||||
if keyID == "testID" {
|
||||
return mts.testKey
|
||||
|
|
@ -80,6 +89,15 @@ func (mts *MockCryptoService) ListKeys(role string) []string {
|
|||
return []string{mts.testKey.ID()}
|
||||
}
|
||||
|
||||
func (mts *MockCryptoService) ListAllKeys() map[string]string {
|
||||
return map[string]string{
|
||||
mts.testKey.ID(): "root",
|
||||
mts.testKey.ID(): "targets",
|
||||
mts.testKey.ID(): "snapshot",
|
||||
mts.testKey.ID(): "timestamp",
|
||||
}
|
||||
}
|
||||
|
||||
func (mts *MockCryptoService) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
|
||||
return nil, "", errors.New("Not implemented")
|
||||
}
|
||||
|
|
@ -115,6 +133,15 @@ func (mts *StrictMockCryptoService) ListKeys(role string) []string {
|
|||
return []string{mts.testKey.ID()}
|
||||
}
|
||||
|
||||
func (mts *StrictMockCryptoService) ListAllKeys() map[string]string {
|
||||
return map[string]string{
|
||||
mts.testKey.ID(): "root",
|
||||
mts.testKey.ID(): "targets",
|
||||
mts.testKey.ID(): "snapshot",
|
||||
mts.testKey.ID(): "timestamp",
|
||||
}
|
||||
}
|
||||
|
||||
// Test signing and ensure the expected signature is added
|
||||
func TestBasicSign(t *testing.T) {
|
||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||
|
|
|
|||
Loading…
Reference in New Issue