Exemple #1
0
func TestEncryption(t *testing.T) {
	s := NewTestImportStore()

	privKey, err := utils.GenerateECDSAKey(rand.Reader)
	originalKey := privKey.Private()
	require.NoError(t, err)

	pemBytes, err := utils.EncryptPrivateKey(privKey, "", "", "")
	require.NoError(t, err)

	in := bytes.NewBuffer(pemBytes)

	_ = ImportKeys(in, []Importer{s}, "", "", passphraseRetriever)
	require.Len(t, s.data, 1)

	shouldBeEnc, ok := s.data[privKey.ID()]
	// we should have got a key imported to this location
	require.True(t, ok)

	// we should fail to parse it without the passphrase
	privKey, err = utils.ParsePEMPrivateKey(shouldBeEnc, "")
	require.Equal(t, err, errors.New("could not decrypt private key"))
	require.Nil(t, privKey)

	// we should succeed to parse it with the passphrase
	privKey, err = utils.ParsePEMPrivateKey(shouldBeEnc, cannedPassphrase)
	require.NoError(t, err)
	require.Equal(t, originalKey, privKey.Private())
}
Exemple #2
0
// path and encrypted key should succeed, tests gun inference from path as well
func TestEncryptedKeyImportSuccess(t *testing.T) {
	s := NewTestImportStore()

	privKey, err := utils.GenerateECDSAKey(rand.Reader)
	originalKey := privKey.Private()
	require.NoError(t, err)

	pemBytes, err := utils.EncryptPrivateKey(privKey, data.CanonicalSnapshotRole, "somegun", cannedPassphrase)
	require.NoError(t, err)

	b, _ := pem.Decode(pemBytes)
	b.Headers["path"] = privKey.ID()
	pemBytes = pem.EncodeToMemory(b)

	in := bytes.NewBuffer(pemBytes)

	_ = ImportKeys(in, []Importer{s}, "", "", passphraseRetriever)
	require.Len(t, s.data, 1)

	keyBytes := s.data[privKey.ID()]

	bFinal, bRest := pem.Decode(keyBytes)
	require.Equal(t, "somegun", bFinal.Headers["gun"])
	require.Len(t, bRest, 0)

	// we should fail to parse it without the passphrase
	privKey, err = utils.ParsePEMPrivateKey(keyBytes, "")
	require.Equal(t, err, errors.New("could not decrypt private key"))
	require.Nil(t, privKey)

	// we should succeed to parse it with the passphrase
	privKey, err = utils.ParsePEMPrivateKey(keyBytes, cannedPassphrase)
	require.NoError(t, err)
	require.Equal(t, originalKey, privKey.Private())
}
Exemple #3
0
// GetPasswdDecryptBytes gets the password to decrypt the given pem bytes.
// Returns the password and private key
func GetPasswdDecryptBytes(passphraseRetriever notary.PassRetriever, pemBytes []byte, name, alias string) (data.PrivateKey, string, error) {
	var (
		passwd  string
		privKey data.PrivateKey
	)
	for attempts := 0; ; attempts++ {
		var (
			giveup bool
			err    error
		)
		if attempts > 10 {
			return nil, "", ErrAttemptsExceeded{}
		}
		passwd, giveup, err = passphraseRetriever(name, alias, false, attempts)
		// Check if the passphrase retriever got an error or if it is telling us to give up
		if giveup || err != nil {
			return nil, "", ErrPasswordInvalid{}
		}

		// Try to convert PEM encoded bytes back to a PrivateKey using the passphrase
		privKey, err = utils.ParsePEMPrivateKey(pemBytes, passwd)
		if err == nil {
			// We managed to parse the PrivateKey. We've succeeded!
			break
		}
	}
	return privKey, passwd, nil
}
Exemple #4
0
// GetKey returns the PrivateKey given a KeyID
func (s *GenericKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) {
	s.Lock()
	defer s.Unlock()

	cachedKeyEntry, ok := s.cachedKeys[keyID]
	if ok {
		return cachedKeyEntry.key, cachedKeyEntry.alias, nil
	}

	role, err := getKeyRole(s.store, keyID)
	if err != nil {
		return nil, "", err
	}

	keyBytes, err := s.store.Get(keyID)
	if err != nil {
		return nil, "", err
	}

	// See if the key is encrypted. If its encrypted we'll fail to parse the private key
	privKey, err := utils.ParsePEMPrivateKey(keyBytes, "")
	if err != nil {
		privKey, _, err = GetPasswdDecryptBytes(s.PassRetriever, keyBytes, keyID, string(role))
		if err != nil {
			return nil, "", err
		}
	}
	s.cachedKeys[keyID] = &cachedKey{alias: role, key: privKey}
	return privKey, role, nil
}
Exemple #5
0
// 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)
}
Exemple #6
0
// checkValidity ensures the fields in the pem headers are valid and parses out the location.
// While importing a collection of keys, errors from this function should result in only the
// current pem block being skipped.
func checkValidity(block *pem.Block) (string, error) {
	// A root key or a delegations key should not have a gun
	// Note that a key that is not any of the canonical roles (except root) is a delegations key and should not have a gun
	switch block.Headers["role"] {
	case tufdata.CanonicalSnapshotRole, tufdata.CanonicalTargetsRole, tufdata.CanonicalTimestampRole:
		// check if the key is missing a gun header or has an empty gun and error out since we don't know what gun it belongs to
		if block.Headers["gun"] == "" {
			logrus.Infof("failed to import key (%s) to store: Cannot have canonical role key without a gun, don't know what gun it belongs to", block.Headers["path"])
			return "", errors.New("invalid key pem block")
		}
	default:
		delete(block.Headers, "gun")
	}

	loc, ok := block.Headers["path"]
	// only if the path isn't specified do we get into this parsing path logic
	if !ok || loc == "" {
		// if the path isn't specified, we will try to infer the path rel to trust dir from the role (and then gun)
		// parse key for the keyID which we will save it by.
		// if the key is encrypted at this point, we will generate an error and continue since we don't know the ID to save it by

		decodedKey, err := utils.ParsePEMPrivateKey(pem.EncodeToMemory(block), "")
		if err != nil {
			logrus.Info("failed to import key to store: Invalid key generated, key may be encrypted and does not contain path header")
			return "", errors.New("invalid key pem block")
		}
		loc = decodedKey.ID()
	}
	return loc, nil
}
Exemple #7
0
// ImportKeys expects an io.Reader containing one or more PEM blocks.
// It reads PEM blocks one at a time until pem.Decode returns a nil
// block.
// Each block is written to the subpath indicated in the "path" PEM
// header. If the file already exists, the file is truncated. Multiple
// adjacent PEMs with the same "path" header are appended together.
func ImportKeys(from io.Reader, to []Importer, fallbackRole string, fallbackGUN string, passRet notary.PassRetriever) error {
	// importLogic.md contains a small flowchart I made to clear up my understand while writing the cases in this function
	// it is very rough, but it may help while reading this piece of code
	data, err := ioutil.ReadAll(from)
	if err != nil {
		return err
	}
	var (
		writeTo string
		toWrite []byte
	)
	for block, rest := pem.Decode(data); block != nil; block, rest = pem.Decode(rest) {
		handleLegacyPath(block)
		setFallbacks(block, fallbackGUN, fallbackRole)

		loc, err := checkValidity(block)
		if err != nil {
			// already logged in checkValidity
			continue
		}

		// the path header is not of any use once we've imported the key so strip it away
		delete(block.Headers, "path")

		// we are now all set for import but let's first encrypt the key
		blockBytes := pem.EncodeToMemory(block)
		// check if key is encrypted, note: if it is encrypted at this point, it will have had a path header
		if privKey, err := utils.ParsePEMPrivateKey(blockBytes, ""); err == nil {
			// Key is not encrypted- ask for a passphrase and encrypt this key
			var chosenPassphrase string
			for attempts := 0; ; attempts++ {
				var giveup bool
				chosenPassphrase, giveup, err = passRet(loc, block.Headers["role"], true, attempts)
				if err == nil {
					break
				}
				if giveup || attempts > 10 {
					return errors.New("maximum number of passphrase attempts exceeded")
				}
			}
			blockBytes, err = utils.EncryptPrivateKey(privKey, block.Headers["role"], block.Headers["gun"], chosenPassphrase)
			if err != nil {
				return errors.New("failed to encrypt key with given passphrase")
			}
		}

		if loc != writeTo {
			// next location is different from previous one. We've finished aggregating
			// data for the previous file. If we have data, write the previous file,
			// clear toWrite and set writeTo to the next path we're going to write
			if toWrite != nil {
				if err = importToStores(to, writeTo, toWrite); err != nil {
					return err
				}
			}
			// set up for aggregating next file's data
			toWrite = nil
			writeTo = loc
		}

		toWrite = append(toWrite, blockBytes...)
	}
	if toWrite != nil { // close out final iteration if there's data left
		return importToStores(to, writeTo, toWrite)
	}
	return nil
}