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
|
data.ECDSAPublicKey
|
||||||
passRetriever passphrase.Retriever
|
passRetriever passphrase.Retriever
|
||||||
slot []byte
|
slot []byte
|
||||||
|
libLoader pkcs11LibLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
type YubikeySigner struct {
|
type YubikeySigner struct {
|
||||||
YubiPrivateKey
|
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{
|
return &YubiPrivateKey{
|
||||||
ECDSAPublicKey: pubKey,
|
ECDSAPublicKey: pubKey,
|
||||||
passRetriever: passRetriever,
|
passRetriever: passRetriever,
|
||||||
slot: slot,
|
slot: slot,
|
||||||
|
libLoader: defaultLoader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,6 +139,10 @@ func (ys *YubikeySigner) Public() crypto.PublicKey {
|
||||||
return publicKey
|
return publicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (y *YubiPrivateKey) setLibLoader(loader pkcs11LibLoader) {
|
||||||
|
y.libLoader = loader
|
||||||
|
}
|
||||||
|
|
||||||
// CryptoSigner returns a crypto.Signer tha wraps the YubiPrivateKey. Needed for
|
// CryptoSigner returns a crypto.Signer tha wraps the YubiPrivateKey. Needed for
|
||||||
// Certificate generation only
|
// Certificate generation only
|
||||||
func (y *YubiPrivateKey) CryptoSigner() crypto.Signer {
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +194,7 @@ func ensurePrivateKeySize(payload []byte) []byte {
|
||||||
|
|
||||||
// addECDSAKey adds a key to the yubikey
|
// addECDSAKey adds a key to the yubikey
|
||||||
func addECDSAKey(
|
func addECDSAKey(
|
||||||
ctx *pkcs11.Ctx,
|
ctx IPKCS11Ctx,
|
||||||
session pkcs11.SessionHandle,
|
session pkcs11.SessionHandle,
|
||||||
privKey data.PrivateKey,
|
privKey data.PrivateKey,
|
||||||
pkcs11KeyID []byte,
|
pkcs11KeyID []byte,
|
||||||
|
|
@ -255,7 +263,7 @@ func addECDSAKey(
|
||||||
return nil
|
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{
|
findTemplate := []*pkcs11.Attribute{
|
||||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||||
pkcs11.NewAttribute(pkcs11.CKA_ID, pkcs11KeyID),
|
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
|
// 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)
|
err := login(ctx, session, passRetriever, pkcs11.CKU_USER, USER_PIN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error logging in: %v", err)
|
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
|
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)
|
err := login(ctx, session, passRetriever, pkcs11.CKU_SO, SO_USER_PIN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -408,7 +416,7 @@ func yubiRemoveKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID []
|
||||||
return nil
|
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)
|
keys = make(map[string]yubiSlot)
|
||||||
findTemplate := []*pkcs11.Attribute{
|
findTemplate := []*pkcs11.Attribute{
|
||||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||||
|
|
@ -501,7 +509,7 @@ func yubiListKeys(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) (keys map[strin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNextEmptySlot(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) ([]byte, error) {
|
func getNextEmptySlot(ctx IPKCS11Ctx, session pkcs11.SessionHandle) ([]byte, error) {
|
||||||
findTemplate := []*pkcs11.Attribute{
|
findTemplate := []*pkcs11.Attribute{
|
||||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||||
}
|
}
|
||||||
|
|
@ -571,6 +579,7 @@ type YubiKeyStore struct {
|
||||||
passRetriever passphrase.Retriever
|
passRetriever passphrase.Retriever
|
||||||
keys map[string]yubiSlot
|
keys map[string]yubiSlot
|
||||||
backupStore trustmanager.KeyStore
|
backupStore trustmanager.KeyStore
|
||||||
|
libLoader pkcs11LibLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewYubiKeyStore returns a YubiKeyStore, given a backup key store to write any
|
// 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,
|
passRetriever: passphraseRetriever,
|
||||||
keys: make(map[string]yubiSlot),
|
keys: make(map[string]yubiSlot),
|
||||||
backupStore: backupStore,
|
backupStore: backupStore,
|
||||||
|
libLoader: defaultLoader,
|
||||||
}
|
}
|
||||||
s.ListKeys() // populate keys field
|
s.ListKeys() // populate keys field
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
@ -593,11 +603,15 @@ func (s YubiKeyStore) Name() string {
|
||||||
return "yubikey"
|
return "yubikey"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *YubiKeyStore) setLibLoader(loader pkcs11LibLoader) {
|
||||||
|
s.libLoader = loader
|
||||||
|
}
|
||||||
|
|
||||||
func (s *YubiKeyStore) ListKeys() map[string]string {
|
func (s *YubiKeyStore) ListKeys() map[string]string {
|
||||||
if len(s.keys) > 0 {
|
if len(s.keys) > 0 {
|
||||||
return buildKeyMap(s.keys)
|
return buildKeyMap(s.keys)
|
||||||
}
|
}
|
||||||
ctx, session, err := SetupHSMEnv(pkcs11Lib)
|
ctx, session, err := SetupHSMEnv(pkcs11Lib, s.libLoader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
||||||
return nil
|
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)
|
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 {
|
if err != nil {
|
||||||
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
||||||
return err
|
return err
|
||||||
|
|
@ -668,7 +682,7 @@ func (s *YubiKeyStore) addKey(
|
||||||
// GetKey retrieves a key from the Yubikey only (it does not look inside the
|
// GetKey retrieves a key from the Yubikey only (it does not look inside the
|
||||||
// backup store)
|
// backup store)
|
||||||
func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) {
|
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 {
|
if err != nil {
|
||||||
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
||||||
return nil, "", err
|
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
|
// RemoveKey deletes a key from the Yubikey only (it does not remove it from the
|
||||||
// backup store)
|
// backup store)
|
||||||
func (s *YubiKeyStore) RemoveKey(keyID string) error {
|
func (s *YubiKeyStore) RemoveKey(keyID string) error {
|
||||||
ctx, session, err := SetupHSMEnv(pkcs11Lib)
|
ctx, session, err := SetupHSMEnv(pkcs11Lib, s.libLoader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
logrus.Debugf("Failed to initialize PKCS11 environment: %s", err.Error())
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -741,18 +755,20 @@ func (s *YubiKeyStore) ImportKey(pemBytes []byte, keyPath string) error {
|
||||||
return s.addKey(privKey.ID(), "root", privKey, false)
|
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.CloseSession(session)
|
||||||
ctx.Finalize()
|
ctx.Finalize()
|
||||||
ctx.Destroy()
|
ctx.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupHSMEnv is a method that depends on the existences
|
// 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 == "" {
|
if libraryPath == "" {
|
||||||
return nil, 0, errors.New("No library found.")
|
return nil, 0, errors.New("No library found.")
|
||||||
}
|
}
|
||||||
p := pkcs11.New(libraryPath)
|
p := libLoader(libraryPath)
|
||||||
|
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return nil, 0, errors.New("Failed to init library")
|
return nil, 0, errors.New("Failed to init library")
|
||||||
|
|
@ -786,7 +802,7 @@ func YubikeyAccessible() bool {
|
||||||
if pkcs11Lib == "" {
|
if pkcs11Lib == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
ctx, session, err := SetupHSMEnv(pkcs11Lib)
|
ctx, session, err := SetupHSMEnv(pkcs11Lib, defaultLoader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -794,7 +810,7 @@ func YubikeyAccessible() bool {
|
||||||
return true
|
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
|
// try default password
|
||||||
err := ctx.Login(session, userFlag, defaultPassw)
|
err := ctx.Login(session, userFlag, defaultPassw)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue