mirror of https://github.com/docker/docs.git
				
				
				
			arbitrary slots working
Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
		
							parent
							
								
									fea898bd34
								
							
						
					
					
						commit
						0fd1fa6ada
					
				| 
						 | 
				
			
			@ -164,7 +164,7 @@ func (cs *CryptoService) ListKeys(role string) []string {
 | 
			
		|||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListKeys returns a list of key IDs valid for the given role
 | 
			
		||||
// ListAllKeys returns a map of key IDs to role
 | 
			
		||||
func (cs *CryptoService) ListAllKeys() map[string]string {
 | 
			
		||||
	res := make(map[string]string)
 | 
			
		||||
	for _, ks := range cs.keyStores {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,16 +39,18 @@ type yubiSlot struct {
 | 
			
		|||
type YubiPrivateKey struct {
 | 
			
		||||
	data.ECDSAPublicKey
 | 
			
		||||
	passRetriever passphrase.Retriever
 | 
			
		||||
	slot          []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type YubikeySigner struct {
 | 
			
		||||
	YubiPrivateKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewYubiPrivateKey(pubKey data.ECDSAPublicKey, passRetriever passphrase.Retriever) *YubiPrivateKey {
 | 
			
		||||
func NewYubiPrivateKey(slot []byte, pubKey data.ECDSAPublicKey, passRetriever passphrase.Retriever) *YubiPrivateKey {
 | 
			
		||||
	return &YubiPrivateKey{
 | 
			
		||||
		ECDSAPublicKey: pubKey,
 | 
			
		||||
		passRetriever:  passRetriever,
 | 
			
		||||
		slot:           slot,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +87,7 @@ func (y *YubiPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts
 | 
			
		|||
	}
 | 
			
		||||
	defer cleanup(ctx, session)
 | 
			
		||||
 | 
			
		||||
	sig, err := sign(ctx, session, YUBIKEY_ROOT_KEY_ID, y.passRetriever, msg)
 | 
			
		||||
	sig, err := sign(ctx, session, y.slot, y.passRetriever, msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to sign using Yubikey: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +96,7 @@ func (y *YubiPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// addECDSAKey adds a key to the yubikey
 | 
			
		||||
func addECDSAKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, privKey data.PrivateKey, pkcs11KeyID []byte, passRetriever passphrase.Retriever) error {
 | 
			
		||||
func addECDSAKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, privKey data.PrivateKey, pkcs11KeyID []byte, passRetriever passphrase.Retriever, role string) error {
 | 
			
		||||
	logrus.Debugf("Got into add key with key: %s\n", privKey.ID())
 | 
			
		||||
 | 
			
		||||
	// TODO(diogo): Figure out CKU_SO with yubikey
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +115,7 @@ func addECDSAKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, privKey data.Pri
 | 
			
		|||
	ecdsaPrivKeyD := ecdsaPrivKey.D.Bytes()
 | 
			
		||||
	logrus.Debugf("Getting D bytes: %v\n", ecdsaPrivKeyD)
 | 
			
		||||
 | 
			
		||||
	template, err := trustmanager.NewCertificate(data.CanonicalRootRole)
 | 
			
		||||
	template, err := trustmanager.NewCertificate(role)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to create the certificate template: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -395,16 +397,80 @@ func listKeys(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) (keys map[string]yu
 | 
			
		|||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getNextEmptySlot(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) ([]byte, error) {
 | 
			
		||||
	findTemplate := []*pkcs11.Attribute{
 | 
			
		||||
		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
 | 
			
		||||
	}
 | 
			
		||||
	attrTemplate := []*pkcs11.Attribute{
 | 
			
		||||
		pkcs11.NewAttribute(pkcs11.CKA_ID, []byte{0}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ctx.FindObjectsInit(session, findTemplate); err != nil {
 | 
			
		||||
		logrus.Debugf("Failed to init: %s\n", err.Error())
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	objs, b, err := ctx.FindObjects(session, numSlots)
 | 
			
		||||
	for err == nil {
 | 
			
		||||
		var o []pkcs11.ObjectHandle
 | 
			
		||||
		o, b, err = ctx.FindObjects(session, numSlots)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if len(o) == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		objs = append(objs, o...)
 | 
			
		||||
	}
 | 
			
		||||
	taken := make([]bool, numSlots)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logrus.Debugf("Failed to find: %s %v\n", err.Error(), b)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, obj := range objs {
 | 
			
		||||
		// Retrieve the public-key material to be able to create a new HSMRSAKey
 | 
			
		||||
		attr, err := ctx.GetAttributeValue(session, obj, attrTemplate)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Debugf("Failed to get Attribute for: %v\n", obj)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 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_ID {
 | 
			
		||||
				if len(a.Value) < 1 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// max 50 slots so a single byte will always represent
 | 
			
		||||
				// all possible slots positions
 | 
			
		||||
				slotNum := int(a.Value[0])
 | 
			
		||||
				if slotNum >= len(taken) {
 | 
			
		||||
					// defensive
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				taken[slotNum] = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < numSlots; i++ {
 | 
			
		||||
		if !taken[i] {
 | 
			
		||||
			return []byte{byte(i)}, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errors.New("Yubikey has no available slots.")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type YubiKeyStore struct {
 | 
			
		||||
	passRetriever passphrase.Retriever
 | 
			
		||||
	keys          map[string]yubiSlot
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewYubiKeyStore(passphraseRetriever passphrase.Retriever) *YubiKeyStore {
 | 
			
		||||
	return &YubiKeyStore{
 | 
			
		||||
	s := &YubiKeyStore{
 | 
			
		||||
		passRetriever: passphraseRetriever,
 | 
			
		||||
		keys:          make(map[string]yubiSlot),
 | 
			
		||||
	}
 | 
			
		||||
	s.ListKeys() // populate keys field
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *YubiKeyStore) ListKeys() map[string]string {
 | 
			
		||||
| 
						 | 
				
			
			@ -424,10 +490,10 @@ func (s *YubiKeyStore) ListKeys() map[string]string {
 | 
			
		|||
	return buildKeyMap(keys)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *YubiKeyStore) AddKey(keyID, alias string, privKey data.PrivateKey) error {
 | 
			
		||||
func (s *YubiKeyStore) AddKey(keyID, role string, privKey data.PrivateKey) error {
 | 
			
		||||
	// We only allow adding root keys for now
 | 
			
		||||
	if alias != data.CanonicalRootRole {
 | 
			
		||||
		return fmt.Errorf("yubikey only supports storing root keys, got %s for key: %s\n", alias, keyID)
 | 
			
		||||
	if role != data.CanonicalRootRole {
 | 
			
		||||
		return fmt.Errorf("yubikey only supports storing root keys, got %s for key: %s\n", role, keyID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx, session, err := SetupHSMEnv(pkcs11Lib)
 | 
			
		||||
| 
						 | 
				
			
			@ -436,8 +502,18 @@ func (s *YubiKeyStore) AddKey(keyID, alias string, privKey data.PrivateKey) erro
 | 
			
		|||
	}
 | 
			
		||||
	defer cleanup(ctx, session)
 | 
			
		||||
 | 
			
		||||
	//return addECDSAKey(ctx, session, privKey, YUBIKEY_ROOT_KEY_ID, s.passRetriever)
 | 
			
		||||
	return addECDSAKey(ctx, session, privKey, []byte{3}, s.passRetriever)
 | 
			
		||||
	if k, ok := s.keys[keyID]; ok {
 | 
			
		||||
		if k.role == role {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	slot, err := getNextEmptySlot(ctx, session)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	logrus.Debugf("Using yubikey slot %v", slot)
 | 
			
		||||
	return addECDSAKey(ctx, session, privKey, slot, s.passRetriever, role)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -447,7 +523,12 @@ func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) {
 | 
			
		|||
	}
 | 
			
		||||
	defer cleanup(ctx, session)
 | 
			
		||||
 | 
			
		||||
	pubKey, alias, err := getECDSAKey(ctx, session, YUBIKEY_ROOT_KEY_ID)
 | 
			
		||||
	key, ok := s.keys[keyID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, "", errors.New("no matching keys found inside of yubikey")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pubKey, alias, err := getECDSAKey(ctx, session, key.slotID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +536,7 @@ func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) {
 | 
			
		|||
	if pubKey.ID() != keyID {
 | 
			
		||||
		return nil, "", fmt.Errorf("expected root key: %s, but found: %s\n", keyID, pubKey.ID())
 | 
			
		||||
	}
 | 
			
		||||
	privKey := NewYubiPrivateKey(*pubKey, s.passRetriever)
 | 
			
		||||
	privKey := NewYubiPrivateKey(key.slotID, *pubKey, s.passRetriever)
 | 
			
		||||
	if privKey == nil {
 | 
			
		||||
		return nil, "", errors.New("could not initialize new YubiPrivateKey")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -469,7 +550,11 @@ func (s *YubiKeyStore) RemoveKey(keyID string) error {
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	defer cleanup(ctx, session)
 | 
			
		||||
	return removeKey(ctx, session, YUBIKEY_ROOT_KEY_ID, s.passRetriever, keyID)
 | 
			
		||||
	key, ok := s.keys[keyID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return errors.New("Key not present in yubikey")
 | 
			
		||||
	}
 | 
			
		||||
	return removeKey(ctx, session, key.slotID, s.passRetriever, keyID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *YubiKeyStore) ExportKey(keyID string) ([]byte, error) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,8 +268,8 @@ func getRawKey(s LimitedFileStore, name string) ([]byte, string, error) {
 | 
			
		|||
	return keyBytes, keyAlias, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the password to decript the given pem bytes.  Return the password,
 | 
			
		||||
// because it is useful for importing
 | 
			
		||||
// GetPasswdDecryptBytes gets the password to decript the given pem bytes.
 | 
			
		||||
// Returns the password and private key
 | 
			
		||||
func GetPasswdDecryptBytes(passphraseRetriever passphrase.Retriever, pemBytes []byte, name, alias string) (data.PrivateKey, string, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		passwd  string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ func (e *Ed25519) ListKeys(role string) []string {
 | 
			
		|||
	return keyIDs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListKeys returns the list of keys IDs for the role
 | 
			
		||||
// ListAllKeys returns the map of keys IDs to role
 | 
			
		||||
func (e *Ed25519) ListAllKeys() map[string]string {
 | 
			
		||||
	keys := make(map[string]string)
 | 
			
		||||
	for id, edKey := range e.keys {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue