// Set determines if we are allowed to set the given key on the Yubikey and // calls through to YubiStore.AddKey if it's valid func (s *YubiImport) Set(name string, bytes []byte) error { block, _ := pem.Decode(bytes) if block == nil { return errors.New("invalid PEM data, could not parse") } role, ok := block.Headers["role"] if !ok { return errors.New("no role found for key") } ki := trustmanager.KeyInfo{ // GUN is ignored by YubiStore Role: role, } privKey, err := utils.ParsePEMPrivateKey(bytes, "") if err != nil { privKey, _, err = trustmanager.GetPasswdDecryptBytes( s.passRetriever, bytes, name, ki.Role, ) if err != nil { return err } } return s.dest.AddKey(ki, privKey) }
// ImportRoleKey imports a private key in PEM format key from a byte array // It prompts for the key's passphrase to verify the data and to determine // the key ID. func (cs *CryptoService) ImportRoleKey(pemBytes []byte, role string, newPassphraseRetriever passphrase.Retriever) error { var alias string var err error if role == data.CanonicalRootRole { alias = role if err = checkRootKeyIsEncrypted(pemBytes); err != nil { return err } } else { // Parse the private key to get the key ID so that we can import it to the correct location privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "") if err != nil { privKey, _, err = trustmanager.GetPasswdDecryptBytes(newPassphraseRetriever, pemBytes, role, string(role)) if err != nil { return err } } // Since we're importing a non-root role, we need to pass the path as an alias alias = filepath.Join(notary.NonRootKeysSubdir, cs.gun, privKey.ID()) // We also need to ensure that the role is properly set in the PEM headers pemBytes, err = trustmanager.KeyToPEM(privKey, role) if 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, alias); err == nil { return nil //bail on the first keystore we import to } } return err }
func TestExportNonRootKeyReencrypt(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) require.NoError(t, err, "failed to create a temporary directory: %s", err) fileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, oldPassphraseRetriever) cs := NewCryptoService(fileStore) pubKey, err := cs.Create(data.CanonicalSnapshotRole, gun, data.ECDSAKey) require.NoError(t, err) snapshotKeyID := pubKey.ID() tempKeyFile, err := ioutil.TempFile("", "notary-test-export-") tempKeyFilePath := tempKeyFile.Name() defer os.Remove(tempKeyFilePath) err = cs.ExportKeyReencrypt(tempKeyFile, snapshotKeyID, newPassphraseRetriever) require.NoError(t, err) tempKeyFile.Close() // Create new repo to test import tempBaseDir2, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir2) require.NoError(t, err, "failed to create a temporary directory: %s", err) fileStore2, err := trustmanager.NewKeyFileStore(tempBaseDir2, newPassphraseRetriever) cs2 := NewCryptoService(fileStore2) keyReader, err := os.Open(tempKeyFilePath) require.NoError(t, err, "could not open key file") pemBytes, err := ioutil.ReadAll(keyReader) require.NoError(t, err, "could not read key file") // Convert to a data.PrivateKey, potentially decrypting the key, and add it to the cryptoservice privKey, _, err := trustmanager.GetPasswdDecryptBytes(newPassphraseRetriever, pemBytes, "", "imported "+data.CanonicalSnapshotRole) require.NoError(t, err) err = cs2.AddKey(data.CanonicalSnapshotRole, gun, privKey) require.NoError(t, err) keyReader.Close() // Look for repo's snapshot key in repo2 // There should be a file named after the key ID of the snapshot key we // imported. snapshotKeyFilename := snapshotKeyID + ".key" _, err = os.Stat(filepath.Join(tempBaseDir2, notary.PrivDir, notary.NonRootKeysSubdir, "docker.com/notary", snapshotKeyFilename)) require.NoError(t, err, "missing snapshot key") // Should be able to unlock the root key with the new password key, alias, err := cs2.GetPrivateKey(snapshotKeyID) require.NoError(t, err, "could not unlock snapshot key") require.Equal(t, data.CanonicalSnapshotRole, alias) require.Equal(t, snapshotKeyID, key.ID()) }
// 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, retriever notary.PassRetriever) 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 fNameTrimmed[len(fNameTrimmed)-5:] == "_root" { if err = CheckRootKeyIsEncrypted(fileBytes); err != nil { return err } } newKeys[fNameTrimmed] = fileBytes } for keyName, pemBytes := range newKeys { // Get the key role information as well as its data.PrivateKey representation _, keyInfo, err := trustmanager.KeyInfoFromPEM(pemBytes, keyName) if err != nil { return err } privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "") if err != nil { privKey, _, err = trustmanager.GetPasswdDecryptBytes(retriever, pemBytes, "", "imported "+keyInfo.Role) if err != nil { return err } } // Add the key to our cryptoservice, will add to the first successful keystore if err = cs.AddKey(keyInfo.Role, keyInfo.Gun, privKey); err != nil { return err } } return nil }
// ImportKey imports a root key into a Yubikey func (s *YubiKeyStore) ImportKey(pemBytes []byte, keyPath string) error { logrus.Debugf("Attempting to import: %s key inside of YubiKeyStore", keyPath) privKey, _, err := trustmanager.GetPasswdDecryptBytes( s.passRetriever, pemBytes, "", "imported root") if err != nil { logrus.Debugf("Failed to get and retrieve a key from: %s", keyPath) return err } if keyPath != data.CanonicalRootRole { return fmt.Errorf("yubikey only supports storing root keys") } _, err = s.addKey(privKey.ID(), "root", privKey) return err }
// Attempt to read an encrypted root key from a file, and return it as a data.PrivateKey func readRootKey(rootKeyFile string, retriever notary.PassRetriever) (data.PrivateKey, error) { keyFile, err := os.Open(rootKeyFile) if err != nil { return nil, fmt.Errorf("Opening file to import as a root key: %v", err) } defer keyFile.Close() pemBytes, err := ioutil.ReadAll(keyFile) if err != nil { return nil, fmt.Errorf("Error reading input root key file: %v", err) } if err = cryptoservice.CheckRootKeyIsEncrypted(pemBytes); err != nil { return nil, err } privKey, _, err := trustmanager.GetPasswdDecryptBytes(retriever, pemBytes, "", data.CanonicalRootRole) if err != nil { return nil, err } return privKey, nil }
func (t *tufCommander) tufInit(cmd *cobra.Command, args []string) error { if len(args) < 1 { cmd.Usage() return fmt.Errorf("Must specify a GUN") } config, err := t.configGetter() if err != nil { return err } gun := args[0] rt, err := getTransport(config, gun, readWrite) if err != nil { return err } trustPin, err := getTrustPinning(config) if err != nil { return err } nRepo, err := notaryclient.NewNotaryRepository( config.GetString("trust_dir"), gun, getRemoteTrustServer(config), rt, t.retriever, trustPin) if err != nil { return err } var rootKeyList []string if t.rootKey != "" { keyFile, err := os.Open(t.rootKey) if err != nil { return fmt.Errorf("Opening file for import: %v", err) } defer keyFile.Close() pemBytes, err := ioutil.ReadAll(keyFile) if err != nil { return fmt.Errorf("Error reading input file: %v", err) } if err = cryptoservice.CheckRootKeyIsEncrypted(pemBytes); err != nil { return err } privKey, _, err := trustmanager.GetPasswdDecryptBytes(t.retriever, pemBytes, "", data.CanonicalRootRole) if err != nil { return err } err = nRepo.CryptoService.AddKey(data.CanonicalRootRole, "", privKey) if err != nil { return fmt.Errorf("Error importing key: %v", err) } rootKeyList = []string{data.PublicKeyFromPrivate(privKey).ID()} } else { rootKeyList = nRepo.CryptoService.ListKeys(data.CanonicalRootRole) } var rootKeyID string if len(rootKeyList) < 1 { cmd.Println("No root keys found. Generating a new root key...") rootPublicKey, err := nRepo.CryptoService.Create(data.CanonicalRootRole, "", data.ECDSAKey) rootKeyID = rootPublicKey.ID() if err != nil { return err } } else { // Choses the first root key available, which is initialization specific // but should return the HW one first. rootKeyID = rootKeyList[0] cmd.Printf("Root key found, using: %s\n", rootKeyID) } if err = nRepo.Initialize([]string{rootKeyID}); err != nil { return err } return nil }
// keysImport imports a private key from a PEM file for a role func (k *keyCommander) keysImport(cmd *cobra.Command, args []string) error { if len(args) != 1 { cmd.Usage() return fmt.Errorf("Must specify input filename for import") } config, err := k.configGetter() if err != nil { return err } ks, err := k.getKeyStores(config, true, false) if err != nil { return err } importFilename := args[0] importFile, err := os.Open(importFilename) if err != nil { return fmt.Errorf("Opening file for import: %v", err) } defer importFile.Close() pemBytes, err := ioutil.ReadAll(importFile) if err != nil { return fmt.Errorf("Error reading input file: %v", err) } pemRole := trustmanager.ReadRoleFromPEM(pemBytes) // If the PEM key doesn't have a role in it, we must have --role set if pemRole == "" && k.keysImportRole == "" { return fmt.Errorf("Could not infer role, and no role was specified for key") } // If both PEM role and a --role are provided and they don't match, error if pemRole != "" && k.keysImportRole != "" && pemRole != k.keysImportRole { return fmt.Errorf("Specified role %s does not match role %s in PEM headers", k.keysImportRole, pemRole) } // Determine which role to add to between PEM headers and --role flag: var importRole string if k.keysImportRole != "" { importRole = k.keysImportRole } else { importRole = pemRole } // If we're importing to targets or snapshot, we need a GUN if (importRole == data.CanonicalTargetsRole || importRole == data.CanonicalSnapshotRole) && k.keysImportGUN == "" { return fmt.Errorf("Must specify GUN for %s key", importRole) } // Root keys must be encrypted if importRole == data.CanonicalRootRole { if err = cryptoservice.CheckRootKeyIsEncrypted(pemBytes); err != nil { return err } } cs := cryptoservice.NewCryptoService(ks...) // Convert to a data.PrivateKey, potentially decrypting the key privKey, err := trustmanager.ParsePEMPrivateKey(pemBytes, "") if err != nil { privKey, _, err = trustmanager.GetPasswdDecryptBytes(k.getRetriever(), pemBytes, "", "imported "+importRole) if err != nil { return err } } err = cs.AddKey(importRole, k.keysImportGUN, privKey) if err != nil { return fmt.Errorf("Error importing key: %v", err) } return nil }