Skip to content

Commit

Permalink
Replaced the PKCS11 trustmanagers with an RPC Interface
Browse files Browse the repository at this point in the history
resolves notaryproject#1289

Signed-off-by: Jan Schintag <jan.schintag@de.ibm.com>
  • Loading branch information
jschintag committed Jul 25, 2019
1 parent bfe872a commit 78a0b1c
Show file tree
Hide file tree
Showing 25 changed files with 1,002 additions and 3,170 deletions.
5 changes: 2 additions & 3 deletions client/client_pkcs11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ package client

import (
"github.com/theupdateframework/notary/trustmanager/pkcs11/common"
"github.com/theupdateframework/notary/trustmanager/pkcs11/yubikey"
"github.com/theupdateframework/notary/trustmanager/pkcs11/externalstore"
)

// clear out all keys
func init() {
yubikey.SetYubikeyKeyMode(0)
ks := yubikey.NewKeyStore()
ks := externalstore.NewKeyStore()
common.SetKeyStore(ks)
if !common.IsAccessible() {
return
Expand Down
13 changes: 1 addition & 12 deletions cmd/notary/integration_pkcs11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,13 @@ import (
"github.com/theupdateframework/notary"
"github.com/theupdateframework/notary/passphrase"
"github.com/theupdateframework/notary/trustmanager/pkcs11/common"
"github.com/theupdateframework/notary/trustmanager/pkcs11/opencryptoki"
"github.com/theupdateframework/notary/trustmanager/pkcs11/yubikey"
"github.com/theupdateframework/notary/tuf/data"
)

var _retriever notary.PassRetriever

func init() {
yubikey.SetYubikeyKeyMode(yubikey.KeymodeNone)
opencryptoki.SetPin("12345670")
opencryptoki.SetSlot(4)
regRetriver := passphrase.PromptRetriever()
_retriever := func(k, a string, c bool, n int) (string, bool, error) {
if k == "Yubikey" {
return regRetriver(k, a, c, n)
}
return testPassphrase, false, nil
}
_retriever := passphrase.PromptRetriever()

// best effort at removing keys here, so nil is fine
s, err := common.NewHardwareStore(nil, _retriever)
Expand Down
14 changes: 6 additions & 8 deletions cmd/notary/keys_pkcs11_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ import (
store "github.com/theupdateframework/notary/storage"
"github.com/theupdateframework/notary/trustmanager"
"github.com/theupdateframework/notary/trustmanager/pkcs11/common"
"github.com/theupdateframework/notary/trustmanager/pkcs11/yubikey"
"github.com/theupdateframework/notary/tuf/data"
)

func TestImportWithYubikey(t *testing.T) {
func TestImportWithHardwareStore(t *testing.T) {

common.SetKeyStore(yubikey.NewKeyStore())
if !common.IsAccessible() {
t.Skip("Must have Yubikey access.")
t.Skip("Must have Hardwarestore access.")
}
setUp(t)
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
Expand All @@ -50,7 +48,7 @@ func TestImportWithYubikey(t *testing.T) {

pubK, err := cs.Create(data.CanonicalRootRole, "ankh", data.ECDSAKey)
require.NoError(t, err)
bID := pubK.ID() // need to check presence in yubikey later
bID := pubK.ID() // need to check presence in hardwarestore later
bytes, err := memStore.Get(pubK.ID())
require.NoError(t, err)
b, _ := pem.Decode(bytes)
Expand Down Expand Up @@ -82,12 +80,12 @@ func TestImportWithYubikey(t *testing.T) {
_, _, err = yks.GetKey(bID)
require.NoError(t, err)
_, _, err = yks.GetKey(cID)
require.Error(t, err) // c is non-root, should not be in yubikey
require.Error(t, err) // c is non-root, should not be in hardwarestore

fileStore, err := store.NewPrivateKeyFileStorage(tempBaseDir, notary.KeyExtension)
require.NoError(t, err)
_, err = fileStore.Get("ankh")
require.Error(t, err) // b should only be in yubikey, not in filestore
require.Error(t, err) // b should only be in hardwarestore, not in filestore

cResult, err := fileStore.Get("morpork")
require.NoError(t, err)
Expand All @@ -99,7 +97,7 @@ func TestImportWithYubikey(t *testing.T) {

func TestGetImporters(t *testing.T) {
if !common.IsAccessible() {
t.Skip("Must have Yubikey access.")
t.Skip("Must have Hardwarestore access.")
}
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
require.NoError(t, err)
Expand Down
Binary file removed cmd/notary/notary
Binary file not shown.
12 changes: 12 additions & 0 deletions trustmanager/pkcs11/common/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package common

import "fmt"

// ErrNoKeysFound is returned when there are no keys in the HardwareStore
type ErrNoKeysFound struct {
HSM string
}

func (e ErrNoKeysFound) Error() string {
return fmt.Sprintf("no keys found in %s", e.HSM)
}
13 changes: 3 additions & 10 deletions trustmanager/pkcs11/common/hardwareprivatekey.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ type HardwarePrivateKey struct {
data.ECDSAPublicKey
passRetriever notary.PassRetriever
slot HardwareSlot
libLoader Pkcs11LibLoader
}

// hardwareSigner wraps a HardwarePrivateKey and implements the crypto.Signer interface
Expand All @@ -38,7 +37,6 @@ func NewHardwarePrivateKey(slot HardwareSlot, pubKey data.ECDSAPublicKey, passRe
ECDSAPublicKey: pubKey,
passRetriever: passRetriever,
slot: slot,
libLoader: DefaultLoader,
}
}

Expand All @@ -52,11 +50,6 @@ func (hs *hardwareSigner) Public() crypto.PublicKey {
return publicKey
}

// SetLibLoader sets up the pkcs library for further usage
func (hpk *HardwarePrivateKey) SetLibLoader(loader Pkcs11LibLoader) {
hpk.libLoader = loader
}

// CryptoSigner returns a crypto.Signer that wraps the HardwarePrivateKey. Needed for
// Certificate generation only
func (hpk *HardwarePrivateKey) CryptoSigner() crypto.Signer {
Expand All @@ -77,15 +70,15 @@ func (hpk HardwarePrivateKey) SignatureAlgorithm() data.SigAlgorithm {
// Sign is a required method of the crypto.Signer interface and the data.PrivateKey
// interface
func (hpk *HardwarePrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
ctx, session, err := hardwareKeyStore.SetupHSMEnv(hpk.libLoader)
session, err := hardwareKeyStore.SetupHSMEnv()
if err != nil {
return nil, err
}
defer Cleanup(ctx, session)
defer hardwareKeyStore.Cleanup(session)

v := signed.Verifiers[data.ECDSASignature]
for i := 0; i < SigAttempts; i++ {
sig, err := hardwareKeyStore.Sign(ctx, session, hpk.slot, hpk.passRetriever, msg)
sig, err := hardwareKeyStore.Sign(session, hpk.slot, hpk.passRetriever, msg)
if err != nil {
return nil, fmt.Errorf("failed to sign using %s: %v", hardwareName, err)
}
Expand Down
33 changes: 13 additions & 20 deletions trustmanager/pkcs11/common/hardwarestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ type HardwareStore struct {
PassRetriever notary.PassRetriever
Keys map[string]HardwareSlot
BackupStore trustmanager.KeyStore
LibLoader Pkcs11LibLoader
}

// NewHardwareStore returns a Store, given a backup key store to write any
Expand All @@ -29,7 +28,6 @@ func NewHardwareStore(backupStore trustmanager.KeyStore, passphraseRetriever not
PassRetriever: passphraseRetriever,
Keys: make(map[string]HardwareSlot),
BackupStore: backupStore,
LibLoader: DefaultLoader,
}
s.ListKeys() // populate keys field
return s, nil
Expand All @@ -40,24 +38,19 @@ func (s HardwareStore) Name() string {
return hardwareName
}

// SetLibLoader sets up the libloader for further usage
func (s *HardwareStore) SetLibLoader(loader Pkcs11LibLoader) {
s.LibLoader = loader
}

// ListKeys returns a list of keys in the hardwarestore
func (s *HardwareStore) ListKeys() map[string]trustmanager.KeyInfo {
if len(s.Keys) > 0 {
return BuildKeyMap(s.Keys)
}
ctx, session, err := hardwareKeyStore.SetupHSMEnv(s.LibLoader)
session, err := hardwareKeyStore.SetupHSMEnv()
if err != nil {
logrus.Debugf("No %s found, using alternative key storage: %s", hardwareName, err.Error())
return nil
}
defer Cleanup(ctx, session)
defer hardwareKeyStore.Cleanup(session)

keys, err := hardwareKeyStore.HardwareListKeys(ctx, session)
keys, err := hardwareKeyStore.HardwareListKeys(session)
if err != nil {
logrus.Debugf("Failed to list key from the %s: %s", hardwareName, err.Error())
return nil
Expand Down Expand Up @@ -93,20 +86,20 @@ func (s *HardwareStore) addKey(keyID string, role data.RoleName, privKey data.Pr
"%s only supports storing root keys, got %s for key: %s", hardwareName, role, keyID)
}

ctx, session, err := hardwareKeyStore.SetupHSMEnv(s.LibLoader)
session, err := hardwareKeyStore.SetupHSMEnv()
if err != nil {
logrus.Debugf("No %s found, using alternative key storage: %s", hardwareName, err.Error())
return false, err
}
defer Cleanup(ctx, session)
defer hardwareKeyStore.Cleanup(session)

if k, ok := s.Keys[keyID]; ok {
if k.Role == role {
return false, nil
}
}

slot, err := hardwareKeyStore.GetNextEmptySlot(ctx, session)
slot, err := hardwareKeyStore.GetNextEmptySlot(session)
if err != nil {
logrus.Debugf("Failed to get an empty %s slot: %s", hardwareName, err.Error())
return false, err
Expand All @@ -117,7 +110,7 @@ func (s *HardwareStore) addKey(keyID string, role data.RoleName, privKey data.Pr
SlotID: slot,
KeyID: keyID,
}
err = hardwareKeyStore.AddECDSAKey(ctx, session, privKey, key, s.PassRetriever, role)
err = hardwareKeyStore.AddECDSAKey(session, privKey, key, s.PassRetriever, role)
if err == nil {
s.Keys[privKey.ID()] = key
return true, nil
Expand All @@ -130,22 +123,22 @@ func (s *HardwareStore) addKey(keyID string, role data.RoleName, privKey data.Pr
// GetKey retrieves a key from the Hardwarestore only (it does not look inside the
// backup store)
func (s *HardwareStore) GetKey(keyID string) (data.PrivateKey, data.RoleName, error) {
ctx, session, err := hardwareKeyStore.SetupHSMEnv(s.LibLoader)
session, err := hardwareKeyStore.SetupHSMEnv()
if err != nil {
logrus.Debugf("No %s found, using alternative key storage: %s", hardwareName, err.Error())
if _, ok := err.(ErrHSMNotPresent); ok {
err = trustmanager.ErrKeyNotFound{KeyID: keyID}
}
return nil, "", err
}
defer Cleanup(ctx, session)
defer hardwareKeyStore.Cleanup(session)

key, ok := s.Keys[keyID]
if !ok {
return nil, "", trustmanager.ErrKeyNotFound{KeyID: keyID}
}

pubKey, alias, err := hardwareKeyStore.GetECDSAKey(ctx, session, key, s.PassRetriever)
pubKey, alias, err := hardwareKeyStore.GetECDSAKey(session, key, s.PassRetriever)
if err != nil {
logrus.Debugf("Failed to get key from slot %s: %s", key.SlotID, err.Error())
return nil, "", err
Expand All @@ -166,19 +159,19 @@ func (s *HardwareStore) GetKey(keyID string) (data.PrivateKey, data.RoleName, er
// RemoveKey deletes a key from the Hardwarestore only (it does not remove it from the
// backup store)
func (s *HardwareStore) RemoveKey(keyID string) error {
ctx, session, err := hardwareKeyStore.SetupHSMEnv(s.LibLoader)
session, err := hardwareKeyStore.SetupHSMEnv()
if err != nil {
logrus.Debugf("No %s found, using alternative key storage: %s", hardwareName, err.Error())
return nil
}
defer Cleanup(ctx, session)
defer hardwareKeyStore.Cleanup(session)

key, ok := s.Keys[keyID]
if !ok {
e := fmt.Sprintf("Key not present in %s", hardwareName)
return errors.New(e)
}
err = hardwareKeyStore.HardwareRemoveKey(ctx, session, key, s.PassRetriever, keyID)
err = hardwareKeyStore.HardwareRemoveKey(session, key, s.PassRetriever, keyID)
if err == nil {
delete(s.Keys, keyID)
} else {
Expand Down
54 changes: 11 additions & 43 deletions trustmanager/pkcs11/common/universal.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func SetKeyStore(ks HardwareSpecificStore) {
// Pkcs11LibLoader defines IPKCS11 which is an interface for wrapping github.com/miekg/pkcs11
type Pkcs11LibLoader func(module string) IPKCS11Ctx

// DefaultLoader returns pkcs11 witha given module
// DefaultLoader returns pkcs11 with a given module
func DefaultLoader(module string) IPKCS11Ctx {
return pkcs11.New(module)
}
Expand Down Expand Up @@ -82,13 +82,14 @@ func (err ErrHSMNotPresent) Error() string {
// HardwareSpecificStore is an interface that defines all the functions, a hardwarespecific keystore needs to implement to work with pkcs11
type HardwareSpecificStore interface {
Name() string
AddECDSAKey(IPKCS11Ctx, pkcs11.SessionHandle, data.PrivateKey, HardwareSlot, notary.PassRetriever, data.RoleName) error
GetECDSAKey(IPKCS11Ctx, pkcs11.SessionHandle, HardwareSlot, notary.PassRetriever) (*data.ECDSAPublicKey, data.RoleName, error)
Sign(IPKCS11Ctx, pkcs11.SessionHandle, HardwareSlot, notary.PassRetriever, []byte) ([]byte, error)
HardwareRemoveKey(IPKCS11Ctx, pkcs11.SessionHandle, HardwareSlot, notary.PassRetriever, string) error
HardwareListKeys(IPKCS11Ctx, pkcs11.SessionHandle) (map[string]HardwareSlot, error)
GetNextEmptySlot(IPKCS11Ctx, pkcs11.SessionHandle) ([]byte, error)
SetupHSMEnv(Pkcs11LibLoader) (IPKCS11Ctx, pkcs11.SessionHandle, error)
AddECDSAKey(pkcs11.SessionHandle, data.PrivateKey, HardwareSlot, notary.PassRetriever, data.RoleName) error
GetECDSAKey(pkcs11.SessionHandle, HardwareSlot, notary.PassRetriever) (*data.ECDSAPublicKey, data.RoleName, error)
Sign(pkcs11.SessionHandle, HardwareSlot, notary.PassRetriever, []byte) ([]byte, error)
HardwareRemoveKey(pkcs11.SessionHandle, HardwareSlot, notary.PassRetriever, string) error
HardwareListKeys(pkcs11.SessionHandle) (map[string]HardwareSlot, error)
GetNextEmptySlot(pkcs11.SessionHandle) ([]byte, error)
SetupHSMEnv() (pkcs11.SessionHandle, error)
Cleanup(pkcs11.SessionHandle)
}

// ErrBackupFailed is returned when a YubiStore fails to back up a key that
Expand All @@ -103,47 +104,14 @@ func (err ErrBackupFailed) Error() string {

// IsAccessible returns true if a Hardwarestore can be accessed
func IsAccessible() bool {
ctx, session, err := hardwareKeyStore.SetupHSMEnv(DefaultLoader)
session, err := hardwareKeyStore.SetupHSMEnv()
if err != nil {
return false
}
defer Cleanup(ctx, session)
defer hardwareKeyStore.Cleanup(session)
return true
}

// Login is responsible for getting a working login on attached hardwarekeystore
func Login(ctx IPKCS11Ctx, session pkcs11.SessionHandle, passRetriever notary.PassRetriever, userFlag uint, defaultPassw string, hardwareName string) error {
err := ctx.Login(session, userFlag, defaultPassw)
if err == nil {
return nil
}

for attempts := 0; ; attempts++ {
var (
giveup bool
err error
user string
)
if userFlag == pkcs11.CKU_SO {
user = "SO Pin"
} else {
user = "User Pin"
}
passwd, giveup, err := passRetriever(user, hardwareName, false, attempts)
if giveup || err != nil {
return trustmanager.ErrPasswordInvalid{}
}
if attempts > 2 {
return trustmanager.ErrAttemptsExceeded{}
}

err = ctx.Login(session, userFlag, passwd)
if err == nil {
return nil
}
}
}

//Cleanup is responsible for cleaning up the pkcs11 session on the hardware
func Cleanup(ctx IPKCS11Ctx, session pkcs11.SessionHandle) {
err := ctx.CloseSession(session)
Expand Down
Loading

0 comments on commit 78a0b1c

Please sign in to comment.