mirror of https://github.com/docker/docs.git
				
				
				
			Replace the pkcs11 library with interfaces for easier testing.
Signed-off-by: Ying Li <ying.li@docker.com> Signed-off-by: David Lawrence <david.lawrence@docker.com> Signed-off-by: Ying Li <ying.li@docker.com> (github: endophage)
This commit is contained in:
		
							parent
							
								
									7108450a21
								
							
						
					
					
						commit
						09c0f9d05b
					
				|  | @ -0,0 +1,40 @@ | |||
| // +build pkcs11
 | ||||
| 
 | ||||
| // an interface around the pkcs11 library, so that things can be mocked out
 | ||||
| // for testing
 | ||||
| 
 | ||||
| package yubikey | ||||
| 
 | ||||
| import "github.com/miekg/pkcs11" | ||||
| 
 | ||||
| // IPKCS11 is an interface for wrapping github.com/miekg/pkcs11
 | ||||
| type pkcs11LibLoader func(module string) IPKCS11Ctx | ||||
| 
 | ||||
| func defaultLoader(module string) IPKCS11Ctx { | ||||
| 	return pkcs11.New(module) | ||||
| } | ||||
| 
 | ||||
| // IPKCS11Ctx is an interface for wrapping the parts of
 | ||||
| // github.com/miekg/pkcs11.Ctx that yubikeystore requires
 | ||||
| type IPKCS11Ctx interface { | ||||
| 	Destroy() | ||||
| 	Initialize() error | ||||
| 	Finalize() error | ||||
| 	GetSlotList(tokenPresent bool) ([]uint, error) | ||||
| 	OpenSession(slotID uint, flags uint) (pkcs11.SessionHandle, error) | ||||
| 	CloseSession(sh pkcs11.SessionHandle) error | ||||
| 	Login(sh pkcs11.SessionHandle, userType uint, pin string) error | ||||
| 	Logout(sh pkcs11.SessionHandle) error | ||||
| 	CreateObject(sh pkcs11.SessionHandle, temp []*pkcs11.Attribute) ( | ||||
| 		pkcs11.ObjectHandle, error) | ||||
| 	DestroyObject(sh pkcs11.SessionHandle, oh pkcs11.ObjectHandle) error | ||||
| 	GetAttributeValue(sh pkcs11.SessionHandle, o pkcs11.ObjectHandle, | ||||
| 		a []*pkcs11.Attribute) ([]*pkcs11.Attribute, error) | ||||
| 	FindObjectsInit(sh pkcs11.SessionHandle, temp []*pkcs11.Attribute) error | ||||
| 	FindObjects(sh pkcs11.SessionHandle, max int) ( | ||||
| 		[]pkcs11.ObjectHandle, bool, error) | ||||
| 	FindObjectsFinal(sh pkcs11.SessionHandle) error | ||||
| 	SignInit(sh pkcs11.SessionHandle, m []*pkcs11.Mechanism, | ||||
| 		o pkcs11.ObjectHandle) error | ||||
| 	Sign(sh pkcs11.SessionHandle, message []byte) ([]byte, error) | ||||
| } | ||||
|  | @ -112,17 +112,21 @@ type YubiPrivateKey struct { | |||
| 	data.ECDSAPublicKey | ||||
| 	passRetriever passphrase.Retriever | ||||
| 	slot          []byte | ||||
| 	libLoader     pkcs11LibLoader | ||||
| } | ||||
| 
 | ||||
| type YubikeySigner struct { | ||||
| 	YubiPrivateKey | ||||
| } | ||||
| 
 | ||||
| func NewYubiPrivateKey(slot []byte, 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, | ||||
| 		libLoader:      defaultLoader, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -135,6 +139,10 @@ func (ys *YubikeySigner) Public() crypto.PublicKey { | |||
| 	return publicKey | ||||
| } | ||||
| 
 | ||||
| func (y *YubiPrivateKey) setLibLoader(loader pkcs11LibLoader) { | ||||
| 	y.libLoader = loader | ||||
| } | ||||
| 
 | ||||
| // CryptoSigner returns a crypto.Signer tha wraps the YubiPrivateKey. Needed for
 | ||||
| // Certificate generation only
 | ||||
| func (y *YubiPrivateKey) CryptoSigner() crypto.Signer { | ||||
|  | @ -153,7 +161,7 @@ func (y YubiPrivateKey) SignatureAlgorithm() data.SigAlgorithm { | |||
| } | ||||
| 
 | ||||
| func (y *YubiPrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) { | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib) | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib, y.libLoader) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | @ -186,7 +194,7 @@ func ensurePrivateKeySize(payload []byte) []byte { | |||
| 
 | ||||
| // addECDSAKey adds a key to the yubikey
 | ||||
| func addECDSAKey( | ||||
| 	ctx *pkcs11.Ctx, | ||||
| 	ctx IPKCS11Ctx, | ||||
| 	session pkcs11.SessionHandle, | ||||
| 	privKey data.PrivateKey, | ||||
| 	pkcs11KeyID []byte, | ||||
|  | @ -255,7 +263,7 @@ func addECDSAKey( | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func getECDSAKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte) (*data.ECDSAPublicKey, string, error) { | ||||
| func getECDSAKey(ctx IPKCS11Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte) (*data.ECDSAPublicKey, string, error) { | ||||
| 	findTemplate := []*pkcs11.Attribute{ | ||||
| 		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), | ||||
| 		pkcs11.NewAttribute(pkcs11.CKA_ID, pkcs11KeyID), | ||||
|  | @ -313,7 +321,7 @@ func getECDSAKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID []by | |||
| } | ||||
| 
 | ||||
| // Sign returns a signature for a given signature request
 | ||||
| func sign(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte, passRetriever passphrase.Retriever, payload []byte) ([]byte, error) { | ||||
| func sign(ctx IPKCS11Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte, passRetriever passphrase.Retriever, payload []byte) ([]byte, error) { | ||||
| 	err := login(ctx, session, passRetriever, pkcs11.CKU_USER, USER_PIN) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error logging in: %v", err) | ||||
|  | @ -367,7 +375,7 @@ func sign(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte, pas | |||
| 	return sig[:], nil | ||||
| } | ||||
| 
 | ||||
| func yubiRemoveKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte, passRetriever passphrase.Retriever, keyID string) error { | ||||
| func yubiRemoveKey(ctx IPKCS11Ctx, session pkcs11.SessionHandle, pkcs11KeyID []byte, passRetriever passphrase.Retriever, keyID string) error { | ||||
| 	err := login(ctx, session, passRetriever, pkcs11.CKU_SO, SO_USER_PIN) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|  | @ -408,7 +416,7 @@ func yubiRemoveKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID [] | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func yubiListKeys(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) (keys map[string]yubiSlot, err error) { | ||||
| func yubiListKeys(ctx IPKCS11Ctx, session pkcs11.SessionHandle) (keys map[string]yubiSlot, err error) { | ||||
| 	keys = make(map[string]yubiSlot) | ||||
| 	findTemplate := []*pkcs11.Attribute{ | ||||
| 		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), | ||||
|  | @ -501,7 +509,7 @@ func yubiListKeys(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) (keys map[strin | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func getNextEmptySlot(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) ([]byte, error) { | ||||
| func getNextEmptySlot(ctx IPKCS11Ctx, session pkcs11.SessionHandle) ([]byte, error) { | ||||
| 	findTemplate := []*pkcs11.Attribute{ | ||||
| 		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), | ||||
| 	} | ||||
|  | @ -571,6 +579,7 @@ type YubiKeyStore struct { | |||
| 	passRetriever passphrase.Retriever | ||||
| 	keys          map[string]yubiSlot | ||||
| 	backupStore   trustmanager.KeyStore | ||||
| 	libLoader     pkcs11LibLoader | ||||
| } | ||||
| 
 | ||||
| // NewYubiKeyStore returns a YubiKeyStore, given a backup key store to write any
 | ||||
|  | @ -582,6 +591,7 @@ func NewYubiKeyStore(backupStore trustmanager.KeyStore, passphraseRetriever pass | |||
| 		passRetriever: passphraseRetriever, | ||||
| 		keys:          make(map[string]yubiSlot), | ||||
| 		backupStore:   backupStore, | ||||
| 		libLoader:     defaultLoader, | ||||
| 	} | ||||
| 	s.ListKeys() // populate keys field
 | ||||
| 	return s, nil | ||||
|  | @ -593,11 +603,15 @@ func (s YubiKeyStore) Name() string { | |||
| 	return "yubikey" | ||||
| } | ||||
| 
 | ||||
| func (s *YubiKeyStore) setLibLoader(loader pkcs11LibLoader) { | ||||
| 	s.libLoader = loader | ||||
| } | ||||
| 
 | ||||
| func (s *YubiKeyStore) ListKeys() map[string]string { | ||||
| 	if len(s.keys) > 0 { | ||||
| 		return buildKeyMap(s.keys) | ||||
| 	} | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib) | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib, s.libLoader) | ||||
| 	if err != nil { | ||||
| 		logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error()) | ||||
| 		return nil | ||||
|  | @ -625,7 +639,7 @@ func (s *YubiKeyStore) addKey( | |||
| 		return fmt.Errorf("yubikey only supports storing root keys, got %s for key: %s", role, keyID) | ||||
| 	} | ||||
| 
 | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib) | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib, s.libLoader) | ||||
| 	if err != nil { | ||||
| 		logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error()) | ||||
| 		return err | ||||
|  | @ -668,7 +682,7 @@ func (s *YubiKeyStore) addKey( | |||
| // GetKey retrieves a key from the Yubikey only (it does not look inside the
 | ||||
| // backup store)
 | ||||
| func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) { | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib) | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib, s.libLoader) | ||||
| 	if err != nil { | ||||
| 		logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error()) | ||||
| 		return nil, "", err | ||||
|  | @ -700,7 +714,7 @@ func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) { | |||
| // RemoveKey deletes a key from the Yubikey only (it does not remove it from the
 | ||||
| // backup store)
 | ||||
| func (s *YubiKeyStore) RemoveKey(keyID string) error { | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib) | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib, s.libLoader) | ||||
| 	if err != nil { | ||||
| 		logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error()) | ||||
| 		return nil | ||||
|  | @ -741,18 +755,20 @@ func (s *YubiKeyStore) ImportKey(pemBytes []byte, keyPath string) error { | |||
| 	return s.addKey(privKey.ID(), "root", privKey, false) | ||||
| } | ||||
| 
 | ||||
| func cleanup(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) { | ||||
| func cleanup(ctx IPKCS11Ctx, session pkcs11.SessionHandle) { | ||||
| 	ctx.CloseSession(session) | ||||
| 	ctx.Finalize() | ||||
| 	ctx.Destroy() | ||||
| } | ||||
| 
 | ||||
| // SetupHSMEnv is a method that depends on the existences
 | ||||
| func SetupHSMEnv(libraryPath string) (*pkcs11.Ctx, pkcs11.SessionHandle, error) { | ||||
| func SetupHSMEnv(libraryPath string, libLoader pkcs11LibLoader) ( | ||||
| 	IPKCS11Ctx, pkcs11.SessionHandle, error) { | ||||
| 
 | ||||
| 	if libraryPath == "" { | ||||
| 		return nil, 0, errors.New("No library found.") | ||||
| 	} | ||||
| 	p := pkcs11.New(libraryPath) | ||||
| 	p := libLoader(libraryPath) | ||||
| 
 | ||||
| 	if p == nil { | ||||
| 		return nil, 0, errors.New("Failed to init library") | ||||
|  | @ -786,7 +802,7 @@ func YubikeyAccessible() bool { | |||
| 	if pkcs11Lib == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib) | ||||
| 	ctx, session, err := SetupHSMEnv(pkcs11Lib, defaultLoader) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
|  | @ -794,7 +810,7 @@ func YubikeyAccessible() bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func login(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, passRetriever passphrase.Retriever, userFlag uint, defaultPassw string) error { | ||||
| func login(ctx IPKCS11Ctx, session pkcs11.SessionHandle, passRetriever passphrase.Retriever, userFlag uint, defaultPassw string) error { | ||||
| 	// try default password
 | ||||
| 	err := ctx.Login(session, userFlag, defaultPassw) | ||||
| 	if err == nil { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue