ask for pin when signing

Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
David Lawrence 2015-10-30 16:23:56 -07:00
parent 53ed60ed89
commit 07f0065152
7 changed files with 184 additions and 42 deletions

View File

@ -24,7 +24,9 @@ import (
"github.com/docker/notary/tuf/store"
)
const maxSize = 5 << 20
const (
maxSize = 5 << 20
)
func init() {
data.SetDefaultExpiryTimes(
@ -96,6 +98,50 @@ func NewTarget(targetName string, targetPath string) (*Target, error) {
return &Target{Name: targetName, Hashes: meta.Hashes, Length: meta.Length}, nil
}
// NewNotaryRepository is a helper method that returns a new notary repository.
// It takes the base directory under where all the trust files will be stored
// (usually ~/.docker/trust/).
func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper,
passphraseRetriever passphrase.Retriever) (*NotaryRepository, error) {
keysPath := filepath.Join(baseDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever)
if err != nil {
return nil, err
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir, fileKeyStore)
if err != nil {
return nil, err
}
yubiKeyStore := api.NewYubiKeyStore(passphraseRetriever)
cryptoService := cryptoservice.NewCryptoService(gun, yubiKeyStore, fileKeyStore)
nRepo := &NotaryRepository{
gun: gun,
baseDir: baseDir,
baseURL: baseURL,
tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)),
CryptoService: cryptoService,
roundTrip: rt,
KeyStoreManager: keyStoreManager,
}
fileStore, err := store.NewFilesystemStore(
nRepo.tufRepoPath,
"metadata",
"json",
"",
)
if err != nil {
return nil, err
}
nRepo.fileStore = fileStore
return nRepo, nil
}
// Initialize creates a new repository by using rootKey as the root Key for the
// TUF repository.
func (r *NotaryRepository) Initialize(rootKeyID string) error {

View File

@ -5,6 +5,7 @@ import (
"fmt"
"math"
"os"
"path/filepath"
"time"
"github.com/docker/notary/keystoremanager"
@ -55,7 +56,12 @@ func certRemove(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -119,7 +125,12 @@ func certList(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}

View File

@ -11,6 +11,7 @@ import (
notaryclient "github.com/docker/notary/client"
"github.com/docker/notary/keystoremanager"
"github.com/docker/notary/pkg/passphrase"
"github.com/docker/notary/trustmanager"
"github.com/spf13/cobra"
)
@ -111,7 +112,12 @@ func keysRemoveKey(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -167,7 +173,12 @@ func keysList(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -220,7 +231,12 @@ func keysGenerateRootKey(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -244,7 +260,12 @@ func keysExport(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -287,7 +308,12 @@ func keysExportRoot(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -322,7 +348,12 @@ func keysImport(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
@ -351,7 +382,12 @@ func keysImportRoot(cmd *cobra.Command, args []string) {
parseConfig()
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, retriever)
keysPath := filepath.Join(trustDir, keystoremanager.PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, retriever)
if err != nil {
fatalf("failed to create private key store in directory: %s", keysPath)
}
keyStoreManager, err := keystoremanager.NewKeyStoreManager(trustDir, fileKeyStore)
if err != nil {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}

View File

@ -60,7 +60,7 @@ func (km *KeyStoreManager) ExportRootKeyReencrypt(dest io.Writer, keyID string,
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
defer os.RemoveAll(tempBaseDir)
tempKeysPath := filepath.Join(tempBaseDir, privDir)
tempKeysPath := filepath.Join(tempBaseDir, PrivDir)
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever)
if err != nil {
return err
@ -178,7 +178,7 @@ func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever
defer os.RemoveAll(tempBaseDir)
// Create temporary keystore to use as a staging area
tempKeysPath := filepath.Join(tempBaseDir, privDir)
tempKeysPath := filepath.Join(tempBaseDir, PrivDir)
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, newPassphraseRetriever)
if err != nil {
return err
@ -190,7 +190,7 @@ func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever
zipWriter := zip.NewWriter(dest)
if err := addKeysToArchive(zipWriter, tempKeyStore, privDir); err != nil {
if err := addKeysToArchive(zipWriter, tempKeyStore, PrivDir); err != nil {
return err
}
@ -224,14 +224,14 @@ func (km *KeyStoreManager) ImportKeysZip(zipReader zip.Reader) error {
// Note that using / as a separator is okay here - the zip
// package guarantees that the separator will be /
if strings.HasPrefix(fNameTrimmed, privDir) {
if strings.HasPrefix(fNameTrimmed, PrivDir) {
if fNameTrimmed[len(fNameTrimmed)-5:] == "_root" {
if err = checkRootKeyIsEncrypted(fileBytes); err != nil {
rc.Close()
return err
}
}
keyName := strings.TrimPrefix(fNameTrimmed, privDir)
keyName := strings.TrimPrefix(fNameTrimmed, PrivDir)
newKeys[keyName] = fileBytes
} else {
// This path inside the zip archive doesn't look like a
@ -283,7 +283,7 @@ func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphras
defer os.RemoveAll(tempBaseDir)
// Create temporary keystore to use as a staging area
tempKeysPath := filepath.Join(tempBaseDir, privDir)
tempKeysPath := filepath.Join(tempBaseDir, PrivDir)
tempKeyStore, err := trustmanager.NewKeyFileStore(tempKeysPath, passphraseRetriever)
if err != nil {
return err
@ -299,7 +299,7 @@ func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphras
return ErrNoKeysFoundForGUN
}
if err := addKeysToArchive(zipWriter, tempKeyStore, privDir); err != nil {
if err := addKeysToArchive(zipWriter, tempKeyStore, PrivDir); err != nil {
return err
}

View File

@ -10,7 +10,6 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/notary/pkg/passphrase"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/signed"
@ -26,7 +25,7 @@ type KeyStoreManager struct {
const (
trustDir = "trusted_certificates"
privDir = "private"
PrivDir = "private"
rsaRootKeySize = 4096 // Used for new root keys
)
@ -56,13 +55,7 @@ func (err ErrRootRotationFail) Error() string {
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error
// if it fails to create the KeyFileStores or load certificates
func NewKeyStoreManager(baseDir string, passphraseRetriever passphrase.Retriever) (*KeyStoreManager, error) {
keysPath := filepath.Join(baseDir, privDir)
keyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever)
if err != nil {
return nil, err
}
func NewKeyStoreManager(baseDir string, keyStore *trustmanager.KeyFileStore) (*KeyStoreManager, error) {
trustPath := filepath.Join(baseDir, trustDir)
// Load all CAs that aren't expired and don't use SHA1

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"
"text/template"
@ -121,8 +122,12 @@ func TestValidateRoot(t *testing.T) {
defer os.RemoveAll(tempBaseDir)
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
keysPath := filepath.Join(tempBaseDir, PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever)
assert.NoError(t, err)
// Create a FileStoreManager
keyStoreManager, err := NewKeyStoreManager(tempBaseDir, passphraseRetriever)
keyStoreManager, err := NewKeyStoreManager(tempBaseDir, fileKeyStore)
assert.NoError(t, err)
// Execute our template
@ -229,8 +234,12 @@ func filestoreWithTwoCerts(t *testing.T, gun, keyAlg string) (
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
assert.NoError(t, err, "failed to create a temporary directory: %s", err)
keysPath := filepath.Join(tempBaseDir, PrivDir)
fileKeyStore, err := trustmanager.NewKeyFileStore(keysPath, passphraseRetriever)
assert.NoError(t, err)
// Create a FileStoreManager
keyStoreManager, err := NewKeyStoreManager(tempBaseDir, passphraseRetriever)
keyStoreManager, err := NewKeyStoreManager(tempBaseDir, fileKeyStore)
assert.NoError(t, err)
certs := make([]*x509.Certificate, 2)

View File

@ -15,13 +15,17 @@ import (
"math/big"
"github.com/Sirupsen/logrus"
"github.com/docker/notary/pkg/passphrase"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/miekg/pkcs11"
)
const pkcs11Lib = "/usr/local/lib/libykcs11.so"
const USER_PIN = "123456"
const (
pkcs11Lib = "/usr/local/lib/libykcs11.so"
USER_PIN = "123456"
SO_USER_PIN = "010203040506070801020304050607080102030405060708"
)
// Hardcoded yubikey PKCS11 ID
var YUBIKEY_ROOT_KEY_ID = []byte{2}
@ -29,14 +33,18 @@ var YUBIKEY_ROOT_KEY_ID = []byte{2}
// YubiPrivateKey represents a private key inside of a yubikey
type YubiPrivateKey struct {
data.ECDSAPublicKey
passRetriever passphrase.Retriever
}
type YubikeySigner struct {
YubiPrivateKey
}
func NewYubiPrivateKey(pubKey data.ECDSAPublicKey) *YubiPrivateKey {
return &YubiPrivateKey{ECDSAPublicKey: pubKey}
func NewYubiPrivateKey(pubKey data.ECDSAPublicKey, passRetriever passphrase.Retriever) *YubiPrivateKey {
return &YubiPrivateKey{
ECDSAPublicKey: pubKey,
passRetriever: passRetriever,
}
}
func (ys *YubikeySigner) Public() crypto.PublicKey {
@ -72,7 +80,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, msg)
sig, err := sign(ctx, session, YUBIKEY_ROOT_KEY_ID, y.passRetriever, msg)
if err != nil {
return nil, fmt.Errorf("failed to sign using Yubikey: %v", err)
}
@ -81,11 +89,11 @@ 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) error {
func addECDSAKey(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, privKey data.PrivateKey, pkcs11KeyID []byte, passRetriever passphrase.Retriever) error {
logrus.Debugf("Got into add key with key: %s\n", privKey.ID())
// TODO(diogo): Figure out CKU_SO with yubikey
err := ctx.Login(session, pkcs11.CKU_SO, "010203040506070801020304050607080102030405060708")
err := login(ctx, session, passRetriever, pkcs11.CKU_SO, SO_USER_PIN)
if err != nil {
return err
}
@ -206,8 +214,8 @@ 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, payload []byte) ([]byte, error) {
err := ctx.Login(session, pkcs11.CKU_USER, USER_PIN)
func sign(ctx *pkcs11.Ctx, 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)
}
@ -253,10 +261,12 @@ func sign(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, pkcs11KeyID, payload []
return sig[:], nil
}
type YubiKeyStore struct{}
type YubiKeyStore struct {
passRetriever passphrase.Retriever
}
func NewYubiKeyStore() *YubiKeyStore {
return &YubiKeyStore{}
func NewYubiKeyStore(passphraseRetriever passphrase.Retriever) *YubiKeyStore {
return &YubiKeyStore{passRetriever: passphraseRetriever}
}
func (s *YubiKeyStore) ListKeys() map[string]string {
@ -285,7 +295,7 @@ func (s *YubiKeyStore) AddKey(keyID, alias string, privKey data.PrivateKey) erro
}
defer cleanup(ctx, session)
return addECDSAKey(ctx, session, privKey, YUBIKEY_ROOT_KEY_ID)
return addECDSAKey(ctx, session, privKey, YUBIKEY_ROOT_KEY_ID, s.passRetriever)
}
func (s *YubiKeyStore) GetKey(keyID string) (data.PrivateKey, string, error) {
@ -303,7 +313,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)
privKey := NewYubiPrivateKey(*pubKey, s.passRetriever)
if privKey == nil {
return nil, "", errors.New("could not initialize new YubiPrivateKey")
}
@ -367,3 +377,40 @@ func SetupHSMEnv(libraryPath string) (*pkcs11.Ctx, pkcs11.SessionHandle, error)
return p, session, nil
}
func login(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, passRetriever passphrase.Retriever, userFlag uint, defaultPassw string) error {
// try default password
err := ctx.Login(session, userFlag, defaultPassw)
if err == nil {
return nil
}
// default failed, ask user for password
for attempts := 0; ; attempts++ {
var (
giveup bool
err error
user string
)
if userFlag == pkcs11.CKU_SO {
user = "SO Pin"
} else {
user = "Pin"
}
passwd, giveup, err := passRetriever(user, "yubikey", false, attempts)
// Check if the passphrase retriever got an error or if it is telling us to give up
if giveup || err != nil {
return trustmanager.ErrPasswordInvalid{}
}
if attempts > 2 {
return trustmanager.ErrAttemptsExceeded{}
}
// Try to convert PEM encoded bytes back to a PrivateKey using the passphrase
err = ctx.Login(session, userFlag, passwd)
if err == nil {
return nil
}
}
return nil
}