Big refactor to make signer use cryptoservices

- Add MemoryFileStore, a partial FileStore implementation that doesn't
  persist on disk.

- Create a KeyStore interface that allows pluggable key store types. Use
  this interface in the cryptoservice implementation.

- Add KeyMemoryStore, which uses MemoryFileStore to provide a KeyStore.

- Add GetKey and DeleteKey functions to cryptoservice.CryptoService.

- Refactor the hardware RSA signing service as a CryptoService.

- Replace custom ed25519 code with cryptoservice.CryptoService.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-07-16 15:13:51 -07:00
parent f5c1d8dbc9
commit 125d72fd77
31 changed files with 715 additions and 661 deletions

2
Godeps/Godeps.json generated
View File

@ -58,7 +58,7 @@
},
{
"ImportPath": "github.com/endophage/gotuf",
"Rev": "82786136d505f582d0f898a2e80c9f6b97b1402c"
"Rev": "f81a3471fd94ba58e004ef9cd9bbbf81bfc565ed"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",

View File

@ -77,10 +77,13 @@ func NewPublicKey(algorithm KeyAlgorithm, public []byte) *PublicKey {
}
}
func PublicKeyFromPrivate(pk PrivateKey) *PublicKey {
return &PublicKey{
func PublicKeyFromPrivate(pk *PrivateKey) *PublicKey {
pub := &PublicKey{
pk.TUFKey,
}
pub.TUFKey.Value.Private = nil
return pub
}
type PrivateKey struct {

View File

@ -29,10 +29,11 @@ func (k SigAlgorithm) String() string {
const (
defaultHashAlgorithm = "sha256"
EDDSASignature SigAlgorithm = "eddsa"
RSAPSSSignature SigAlgorithm = "rsapss"
ECDSASignature SigAlgorithm = "ecdsa"
PyCryptoSignature SigAlgorithm = "pycrypto-pkcs#1 pss"
EDDSASignature SigAlgorithm = "eddsa"
RSAPSSSignature SigAlgorithm = "rsapss"
RSAPKCS1v15Signature SigAlgorithm = "rsapkcs1v15"
ECDSASignature SigAlgorithm = "ecdsa"
PyCryptoSignature SigAlgorithm = "pycrypto-pkcs#1 pss"
ED25519Key KeyAlgorithm = "ed25519"
RSAKey KeyAlgorithm = "rsa"

View File

@ -18,17 +18,17 @@ var (
type KeyDB struct {
roles map[string]*data.Role
keys map[string]*data.PublicKey
keys map[string]data.Key
}
func NewDB() *KeyDB {
return &KeyDB{
roles: make(map[string]*data.Role),
keys: make(map[string]*data.PublicKey),
keys: make(map[string]data.Key),
}
}
func (db *KeyDB) AddKey(k *data.PublicKey) {
func (db *KeyDB) AddKey(k data.Key) {
db.keys[k.ID()] = k
}
@ -51,7 +51,7 @@ func (db *KeyDB) AddRole(r *data.Role) error {
return nil
}
func (db *KeyDB) GetKey(id string) *data.PublicKey {
func (db *KeyDB) GetKey(id string) data.Key {
return db.keys[id]
}

View File

@ -8,7 +8,7 @@ import (
"github.com/endophage/gotuf/data"
)
// Ed25519 implements a simple in memory keystore and trust service
// Ed25519 implements a simple in memory cryptosystem for ED25519 keys
type Ed25519 struct {
keys map[string]*data.PrivateKey
}
@ -19,22 +19,21 @@ func NewEd25519() *Ed25519 {
}
}
// addKey allows you to add a private key to the trust service
func (trust *Ed25519) addKey(k *data.PrivateKey) {
trust.keys[k.ID()] = k
// addKey allows you to add a private key
func (e *Ed25519) addKey(k *data.PrivateKey) {
e.keys[k.ID()] = k
}
func (trust *Ed25519) RemoveKey(keyID string) {
delete(trust.keys, keyID)
func (e *Ed25519) RemoveKey(keyID string) error {
delete(e.keys, keyID)
return nil
}
func (trust *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
func (e *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, error) {
signatures := make([]data.Signature, 0, len(keyIDs))
for _, kID := range keyIDs {
priv := [ed25519.PrivateKeySize]byte{}
pub := [ed25519.PublicKeySize]byte{}
copy(priv[:], trust.keys[kID].Private())
copy(pub[:], trust.keys[kID].Public())
copy(priv[:], e.keys[kID].Private())
sig := ed25519.Sign(&priv, toSign)
signatures = append(signatures, data.Signature{
KeyID: kID,
@ -46,7 +45,7 @@ func (trust *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, er
}
func (trust *Ed25519) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) {
func (e *Ed25519) Create(role string, algorithm data.KeyAlgorithm) (data.Key, error) {
if algorithm != data.ED25519Key {
return nil, errors.New("only ED25519 supported by this cryptoservice")
}
@ -57,16 +56,20 @@ func (trust *Ed25519) Create(role string, algorithm data.KeyAlgorithm) (*data.Pu
}
public := data.NewPublicKey(data.ED25519Key, pub[:])
private := data.NewPrivateKey(data.ED25519Key, pub[:], priv[:])
trust.addKey(private)
e.addKey(private)
return public, nil
}
func (trust *Ed25519) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
func (e *Ed25519) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
k := make(map[string]*data.PublicKey)
for _, kID := range keyIDs {
if key, ok := trust.keys[kID]; ok {
k[kID] = data.PublicKeyFromPrivate(*key)
if key, ok := e.keys[kID]; ok {
k[kID] = data.PublicKeyFromPrivate(key)
}
}
return k, nil
}
func (e *Ed25519) GetKey(keyID string) data.Key {
return data.PublicKeyFromPrivate(e.keys[keyID])
}

View File

@ -20,7 +20,13 @@ type KeyService interface {
// the private key into the appropriate signing service.
// The role isn't currently used for anything, but it's here to support
// future features
Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error)
Create(role string, algorithm data.KeyAlgorithm) (data.Key, error)
// GetKey retrieves the public key if present, otherwise it returns nil
GetKey(keyID string) data.Key
// RemoveKey deletes the specified key
RemoveKey(keyID string) error
}
// CryptoService defines a unified Signing and Key Service as this

View File

@ -7,7 +7,7 @@ import (
// Sign takes a data.Signed and a key, calculated and adds the signature
// to the data.Signed
func Sign(service CryptoService, s *data.Signed, keys ...*data.PublicKey) error {
func Sign(service CryptoService, s *data.Signed, keys ...data.Key) error {
logrus.Debugf("sign called with %d keys", len(keys))
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
keyIDMemb := make(map[string]struct{})

View File

@ -27,13 +27,19 @@ func (mts *MockCryptoService) Sign(keyIDs []string, _ []byte) ([]data.Signature,
return sigs, nil
}
func (mts *MockCryptoService) Create(_ string, _ data.KeyAlgorithm) (*data.PublicKey, error) {
func (mts *MockCryptoService) Create(_ string, _ data.KeyAlgorithm) (data.Key, error) {
return &mts.testKey, nil
}
func (mts *MockCryptoService) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
keys := map[string]*data.PublicKey{"testID": &mts.testKey}
return keys, nil
func (mts *MockCryptoService) GetKey(keyID string) data.Key {
if keyID == "testID" {
return &mts.testKey
}
return nil
}
func (mts *MockCryptoService) RemoveKey(keyID string) error {
return nil
}
var _ CryptoService = &MockCryptoService{}

View File

@ -19,10 +19,11 @@ import (
// can be injected into a verificationService. For testing and configuration
// purposes, it will not be used by default.
var Verifiers = map[data.SigAlgorithm]Verifier{
data.RSAPSSSignature: RSAPSSVerifier{},
data.PyCryptoSignature: RSAPyCryptoVerifier{},
data.ECDSASignature: ECDSAVerifier{},
data.EDDSASignature: Ed25519Verifier{},
data.RSAPSSSignature: RSAPSSVerifier{},
data.RSAPKCS1v15Signature: RSAPKCS1v15Verifier{},
data.PyCryptoSignature: RSAPyCryptoVerifier{},
data.ECDSASignature: ECDSAVerifier{},
data.EDDSASignature: Ed25519Verifier{},
}
// RegisterVerifier provides a convenience function for init() functions
@ -78,11 +79,7 @@ func verifyPSS(key interface{}, digest, sig []byte) error {
return nil
}
// RSAPSSVerifier checks RSASSA-PSS signatures
type RSAPSSVerifier struct{}
// Verify does the actual check.
func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
func getRSAPubKey(key data.Key) (crypto.PublicKey, error) {
algorithm := key.Algorithm()
var pubKey crypto.PublicKey
@ -91,12 +88,12 @@ func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
pemCert, _ := pem.Decode([]byte(key.Public()))
if pemCert == nil {
logrus.Infof("failed to decode PEM-encoded x509 certificate")
return ErrInvalid
return nil, ErrInvalid
}
cert, err := x509.ParseCertificate(pemCert.Bytes)
if err != nil {
logrus.Infof("failed to parse x509 certificate: %s\n", err)
return ErrInvalid
return nil, ErrInvalid
}
pubKey = cert.PublicKey
case data.RSAKey:
@ -104,11 +101,24 @@ func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
pubKey, err = x509.ParsePKIXPublicKey(key.Public())
if err != nil {
logrus.Infof("failed to parse public key: %s\n", err)
return ErrInvalid
return nil, ErrInvalid
}
default:
logrus.Infof("invalid key type for RSAPSS verifier: %s", algorithm)
return ErrInvalid
return nil, ErrInvalid
}
return pubKey, nil
}
// RSAPSSVerifier checks RSASSA-PSS signatures
type RSAPSSVerifier struct{}
// Verify does the actual check.
func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
pubKey, err := getRSAPubKey(key)
if err != nil {
return err
}
digest := sha256.Sum256(msg)
@ -116,6 +126,29 @@ func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
return verifyPSS(pubKey, digest[:], sig)
}
// RSAPKCS1v15SVerifier checks RSA PKCS1v15 signatures
type RSAPKCS1v15Verifier struct{}
func (v RSAPKCS1v15Verifier) Verify(key data.Key, sig []byte, msg []byte) error {
pubKey, err := getRSAPubKey(key)
if err != nil {
return err
}
digest := sha256.Sum256(msg)
rsaPub, ok := pubKey.(*rsa.PublicKey)
if !ok {
logrus.Infof("value was not an RSA public key")
return ErrInvalid
}
if err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig); err != nil {
logrus.Errorf("Failed verification: %s", err.Error())
return ErrInvalid
}
return nil
}
// RSAPSSVerifier checks RSASSA-PSS signatures
type RSAPyCryptoVerifier struct{}

File diff suppressed because one or more lines are too long

View File

@ -19,10 +19,10 @@ type VerifySuite struct{}
var _ = Suite(&VerifySuite{})
func (VerifySuite) Test(c *C) {
trust := NewEd25519()
cryptoService := NewEd25519()
type test struct {
name string
keys []*data.PublicKey
keys []data.Key
roles map[string]*data.Role
s *data.Signed
ver int
@ -77,8 +77,8 @@ func (VerifySuite) Test(c *C) {
{
name: "more than enough signatures",
mut: func(t *test) {
k, _ := trust.Create("root", data.ED25519Key)
Sign(trust, t.s, k)
k, _ := cryptoService.Create("root", data.ED25519Key)
Sign(cryptoService, t.s, k)
t.keys = append(t.keys, k)
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.ID())
},
@ -94,15 +94,15 @@ func (VerifySuite) Test(c *C) {
{
name: "unknown key",
mut: func(t *test) {
k, _ := trust.Create("root", data.ED25519Key)
Sign(trust, t.s, k)
k, _ := cryptoService.Create("root", data.ED25519Key)
Sign(cryptoService, t.s, k)
},
},
{
name: "unknown key below threshold",
mut: func(t *test) {
k, _ := trust.Create("root", data.ED25519Key)
Sign(trust, t.s, k)
k, _ := cryptoService.Create("root", data.ED25519Key)
Sign(cryptoService, t.s, k)
t.roles["root"].Threshold = 2
},
err: ErrRoleThreshold,
@ -110,16 +110,16 @@ func (VerifySuite) Test(c *C) {
{
name: "unknown keys in db",
mut: func(t *test) {
k, _ := trust.Create("root", data.ED25519Key)
Sign(trust, t.s, k)
k, _ := cryptoService.Create("root", data.ED25519Key)
Sign(cryptoService, t.s, k)
t.keys = append(t.keys, k)
},
},
{
name: "unknown keys in db below threshold",
mut: func(t *test) {
k, _ := trust.Create("root", data.ED25519Key)
Sign(trust, t.s, k)
k, _ := cryptoService.Create("root", data.ED25519Key)
Sign(cryptoService, t.s, k)
t.keys = append(t.keys, k)
t.roles["root"].Threshold = 2
},
@ -156,15 +156,15 @@ func (VerifySuite) Test(c *C) {
t.typ = data.TUFTypes[t.role]
}
if t.keys == nil && t.s == nil {
k, _ := trust.Create("root", data.ED25519Key)
k, _ := cryptoService.Create("root", data.ED25519Key)
meta := &signedMeta{Type: t.typ, Version: t.ver, Expires: t.exp.Format("2006-01-02 15:04:05 MST")}
b, err := cjson.Marshal(meta)
c.Assert(err, IsNil)
s := &data.Signed{Signed: b}
Sign(trust, s, k)
Sign(cryptoService, s, k)
t.s = s
t.keys = []*data.PublicKey{k}
t.keys = []data.Key{k}
}
if t.roles == nil {
t.roles = map[string]*data.Role{

View File

@ -579,7 +579,7 @@ func (tr *TufRepo) SignTimestamp(expires time.Time, cryptoService signed.CryptoS
}
func (tr TufRepo) sign(signedData *data.Signed, role data.Role, cryptoService signed.CryptoService) (*data.Signed, error) {
ks := make([]*data.PublicKey, 0, len(role.KeyIDs))
ks := make([]data.Key, 0, len(role.KeyIDs))
for _, kid := range role.KeyIDs {
k := tr.keysDB.GetKey(kid)
if k == nil {

View File

@ -14,9 +14,10 @@ import (
"google.golang.org/grpc/credentials"
_ "github.com/docker/distribution/health"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/signer/keys"
"github.com/docker/notary/trustmanager"
"github.com/endophage/gotuf/data"
"github.com/miekg/pkcs11"
@ -68,7 +69,7 @@ func main() {
}
tlsConfig.Rand = rand.Reader
sigServices := make(signer.SigningServiceIndex)
cryptoServices := make(signer.CryptoServiceIndex)
if pkcs11Lib != "" {
if pin == "" {
@ -79,14 +80,18 @@ func main() {
defer cleanup(ctx, session)
sigServices[data.RSAKey] = api.NewRSASigningService(ctx, session)
cryptoServices[data.RSAKey] = api.NewRSAHardwareCryptoService(ctx, session)
}
sigServices[data.ED25519Key] = api.EdDSASigningService{KeyDB: keys.NewKeyDB()}
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
cryptoServices[data.ED25519Key] = cryptoService
cryptoServices[data.ECDSAKey] = cryptoService
//RPC server setup
kms := &api.KeyManagementServer{SigServices: sigServices}
ss := &api.SignerServer{SigServices: sigServices}
kms := &api.KeyManagementServer{CryptoServices: cryptoServices}
ss := &api.SignerServer{CryptoServices: cryptoServices}
grpcServer := grpc.NewServer()
pb.RegisterKeyManagementServer(grpcServer, kms)
@ -105,7 +110,7 @@ func main() {
//HTTP server setup
server := http.Server{
Addr: _Addr,
Handler: api.Handlers(sigServices),
Handler: api.Handlers(cryptoServices),
TLSConfig: tlsConfig,
}

View File

@ -25,16 +25,16 @@ const (
type CryptoService struct {
gun string
passphrase string
keyStore *trustmanager.KeyFileStore
keyStore trustmanager.KeyStore
}
// NewCryptoService returns an instance of CryptoService
func NewCryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *CryptoService {
func NewCryptoService(gun string, keyStore trustmanager.KeyStore, passphrase string) *CryptoService {
return &CryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}
}
// Create is used to generate keys for targets, snapshots and timestamps
func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) {
func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (data.Key, error) {
var privKey *data.PrivateKey
var err error
@ -64,16 +64,27 @@ func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (*dat
if err != nil {
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
}
return data.PublicKeyFromPrivate(*privKey), nil
return data.PublicKeyFromPrivate(privKey), nil
}
// GetKey returns a key by ID
func (ccs *CryptoService) GetKey(keyID string) data.Key {
key, err := ccs.keyStore.GetKey(keyID)
if err != nil {
return nil
}
return data.PublicKeyFromPrivate(key)
}
// RemoveKey deletes a key by ID
func (ccs *CryptoService) RemoveKey(keyID string) error {
return ccs.keyStore.Remove(keyID)
}
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
// errors to sign and expects the called to validate if the number of returned
// signatures is adequate.
func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
var sha256Sum [sha256.Size]byte
sha256Computed := false
signatures := make([]data.Signature, 0, len(keyIDs))
for _, keyid := range keyIDs {
// ccs.gun will be empty if this is the root key
@ -103,18 +114,10 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
switch algorithm {
case data.RSAKey:
if !sha256Computed {
sha256Sum = sha256.Sum256(payload)
sha256Computed = true
}
sig, err = rsaSign(privKey, crypto.SHA256, sha256Sum[:])
sig, err = rsaSign(privKey, payload)
sigAlgorithm = data.RSAPSSSignature
case data.ECDSAKey:
if !sha256Computed {
sha256Sum = sha256.Sum256(payload)
sha256Computed = true
}
sig, err = ecdsaSign(privKey, sha256Sum[:])
sig, err = ecdsaSign(privKey, payload)
sigAlgorithm = data.ECDSASignature
case data.ED25519Key:
// ED25519 does not operate on a SHA256 hash
@ -139,11 +142,13 @@ func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signatur
return signatures, nil
}
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
func rsaSign(privKey *data.PrivateKey, message []byte) ([]byte, error) {
if privKey.Algorithm() != data.RSAKey {
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
}
hashed := sha256.Sum256(message)
// Create an rsa.PrivateKey out of the private key bytes
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(privKey.Private())
if err != nil {
@ -151,7 +156,7 @@ func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte,
}
// Use the RSA key to RSASSA-PSS sign the data
sig, err := rsa.SignPSS(rand.Reader, rsaPrivKey, hash, hashed[:], &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
sig, err := rsa.SignPSS(rand.Reader, rsaPrivKey, crypto.SHA256, hashed[:], &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
if err != nil {
return nil, err
}
@ -159,11 +164,13 @@ func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte,
return sig, nil
}
func ecdsaSign(privKey *data.PrivateKey, hashed []byte) ([]byte, error) {
func ecdsaSign(privKey *data.PrivateKey, message []byte) ([]byte, error) {
if privKey.Algorithm() != data.ECDSAKey {
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
}
hashed := sha256.Sum256(message)
// Create an ecdsa.PrivateKey out of the private key bytes
ecdsaPrivKey, err := x509.ParseECPrivateKey(privKey.Private())
if err != nil {

View File

@ -36,7 +36,7 @@ func (ucs *UnlockedCryptoService) ID() string {
// PublicKey Returns the public key associated with the private key
func (ucs *UnlockedCryptoService) PublicKey() *data.PublicKey {
return data.PublicKeyFromPrivate(*ucs.PrivKey)
return data.PublicKeyFromPrivate(ucs.PrivKey)
}
// GenerateCertificate generates an X509 Certificate from a template, given a GUN

View File

@ -30,7 +30,7 @@ func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.
}
err = store.SetTimestampKey(gun, key.Algorithm(), key.Public())
if err == nil {
return &key.TUFKey, nil
return data.NewTUFKey(key.Algorithm(), key.Public(), key.Private()), nil
}
if _, ok := err.(*storage.ErrTimestampKeyExists); ok {

View File

@ -7,33 +7,34 @@ import (
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/keys"
"github.com/endophage/gotuf/data"
"github.com/endophage/gotuf/signed"
"github.com/gorilla/mux"
pb "github.com/docker/notary/proto"
)
// Handlers sets up all the handers for the routes, injecting a specific SigningService object for them to use
func Handlers(sigServices signer.SigningServiceIndex) *mux.Router {
// Handlers sets up all the handers for the routes, injecting a specific CryptoService object for them to use
func Handlers(cryptoServices signer.CryptoServiceIndex) *mux.Router {
r := mux.NewRouter()
r.Methods("GET").Path("/{ID}").Handler(KeyInfo(sigServices))
r.Methods("POST").Path("/new/{Algorithm}").Handler(CreateKey(sigServices))
r.Methods("POST").Path("/delete").Handler(DeleteKey(sigServices))
r.Methods("POST").Path("/sign").Handler(Sign(sigServices))
r.Methods("GET").Path("/{ID}").Handler(KeyInfo(cryptoServices))
r.Methods("POST").Path("/new/{Algorithm}").Handler(CreateKey(cryptoServices))
r.Methods("POST").Path("/delete").Handler(DeleteKey(cryptoServices))
r.Methods("POST").Path("/sign").Handler(Sign(cryptoServices))
return r
}
// getSigningService handles looking up the correct signing service, given the
// getCryptoService handles looking up the correct signing service, given the
// algorithm specified in the HTTP request. If the algorithm isn't specified
// or isn't supported, an error is returned to the client and this function
// returns a nil SigningService
func getSigningService(w http.ResponseWriter, algorithm string, sigServices signer.SigningServiceIndex) signer.SigningService {
// returns a nil CryptoService
func getCryptoService(w http.ResponseWriter, algorithm string, cryptoServices signer.CryptoServiceIndex) signed.CryptoService {
if algorithm == "" {
http.Error(w, "algorithm not specified", http.StatusBadRequest)
return nil
}
service := sigServices[data.KeyAlgorithm(algorithm)]
service := cryptoServices[data.KeyAlgorithm(algorithm)]
if service == nil {
http.Error(w, "algorithm "+algorithm+" not supported", http.StatusBadRequest)
@ -44,11 +45,11 @@ func getSigningService(w http.ResponseWriter, algorithm string, sigServices sign
}
// KeyInfo returns a Handler that given a specific Key ID param, returns the public key bits of that key
func KeyInfo(sigServices signer.SigningServiceIndex) http.Handler {
func KeyInfo(cryptoServices signer.CryptoServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
key, _, err := FindKeyByID(sigServices, &pb.KeyID{ID: vars["ID"]})
tufKey, _, err := FindKeyByID(cryptoServices, &pb.KeyID{ID: vars["ID"]})
if err != nil {
switch err {
// If we received an ErrInvalidKeyID, the key doesn't exist, return 404
@ -63,34 +64,48 @@ func KeyInfo(sigServices signer.SigningServiceIndex) http.Handler {
return
}
}
key := &pb.PublicKey{
KeyInfo: &pb.KeyInfo{
KeyID: &pb.KeyID{ID: tufKey.ID()},
Algorithm: &pb.Algorithm{Algorithm: tufKey.Algorithm().String()},
},
PublicKey: tufKey.Public(),
}
json.NewEncoder(w).Encode(key)
return
})
}
// CreateKey returns a handler that generates a new
func CreateKey(sigServices signer.SigningServiceIndex) http.Handler {
func CreateKey(cryptoServices signer.CryptoServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sigService := getSigningService(w, vars["Algorithm"], sigServices)
if sigService == nil {
// Error handled inside getSigningService
cryptoService := getCryptoService(w, vars["Algorithm"], cryptoServices)
if cryptoService == nil {
// Error handled inside getCryptoService
return
}
key, err := sigService.CreateKey()
tufKey, err := cryptoService.Create("", data.KeyAlgorithm(vars["Algorithm"]))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
key := &pb.PublicKey{
KeyInfo: &pb.KeyInfo{
KeyID: &pb.KeyID{ID: tufKey.ID()},
Algorithm: &pb.Algorithm{Algorithm: tufKey.Algorithm().String()},
},
PublicKey: tufKey.Public(),
}
json.NewEncoder(w).Encode(key)
return
})
}
// DeleteKey returns a handler that delete a specific KeyID
func DeleteKey(sigServices signer.SigningServiceIndex) http.Handler {
func DeleteKey(cryptoServices signer.CryptoServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var keyID *pb.KeyID
err := json.NewDecoder(r.Body).Decode(&keyID)
@ -102,7 +117,7 @@ func DeleteKey(sigServices signer.SigningServiceIndex) http.Handler {
return
}
_, sigService, err := FindKeyByID(sigServices, keyID)
_, cryptoService, err := FindKeyByID(cryptoServices, keyID)
if err != nil {
switch err {
@ -119,9 +134,7 @@ func DeleteKey(sigServices signer.SigningServiceIndex) http.Handler {
}
}
_, err = sigService.DeleteKey(keyID)
if err != nil {
if err = cryptoService.RemoveKey(keyID.ID); err != nil {
switch err {
// If we received an ErrInvalidKeyID, the key doesn't exist, return 404
case keys.ErrInvalidKeyID:
@ -141,7 +154,7 @@ func DeleteKey(sigServices signer.SigningServiceIndex) http.Handler {
}
// Sign returns a handler that is able to perform signatures on a given blob
func Sign(sigServices signer.SigningServiceIndex) http.Handler {
func Sign(cryptoServices signer.CryptoServiceIndex) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var sigRequest *pb.SignatureRequest
err := json.NewDecoder(r.Body).Decode(&sigRequest)
@ -154,7 +167,7 @@ func Sign(sigServices signer.SigningServiceIndex) http.Handler {
return
}
_, sigService, err := FindKeyByID(sigServices, sigRequest.KeyID)
tufKey, cryptoService, err := FindKeyByID(cryptoServices, sigRequest.KeyID)
if err == keys.ErrInvalidKeyID {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
@ -166,23 +179,18 @@ func Sign(sigServices signer.SigningServiceIndex) http.Handler {
return
}
signer, err := sigService.Signer(sigRequest.KeyID)
if err == keys.ErrInvalidKeyID {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(err.Error()))
return
} else if err != nil {
// We got an unexpected error
signatures, err := cryptoService.Sign([]string{sigRequest.KeyID.ID}, sigRequest.Content)
if err != nil || len(signatures) != 1 {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
signature, err := signer.Sign(sigRequest)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
signature := &pb.Signature{
KeyInfo: &pb.KeyInfo{
KeyID: &pb.KeyID{ID: tufKey.ID()},
Algorithm: &pb.Algorithm{Algorithm: tufKey.Algorithm().String()},
},
Content: signatures[0].Signature,
}
json.NewEncoder(w).Encode(signature)

View File

@ -11,9 +11,10 @@ import (
"strings"
"testing"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/signer/keys"
"github.com/docker/notary/trustmanager"
"github.com/endophage/gotuf/data"
"github.com/miekg/pkcs11"
"github.com/stretchr/testify/assert"
@ -22,14 +23,12 @@ import (
)
var (
server *httptest.Server
reader io.Reader
hsmSigService *api.RSASigningService
softwareSigService *api.EdDSASigningService
deleteKeyBaseURL string
createKeyBaseURL string
keyInfoBaseURL string
signBaseURL string
server *httptest.Server
reader io.Reader
deleteKeyBaseURL string
createKeyBaseURL string
keyInfoBaseURL string
signBaseURL string
)
func SetupHSMEnv(t *testing.T) (*pkcs11.Ctx, pkcs11.SessionHandle) {
@ -65,8 +64,8 @@ func SetupHSMEnv(t *testing.T) (*pkcs11.Ctx, pkcs11.SessionHandle) {
return p, session
}
func setup(sigServices signer.SigningServiceIndex) {
server = httptest.NewServer(api.Handlers(sigServices))
func setup(cryptoServices signer.CryptoServiceIndex) {
server = httptest.NewServer(api.Handlers(cryptoServices))
deleteKeyBaseURL = fmt.Sprintf("%s/delete", server.URL)
createKeyBaseURL = fmt.Sprintf("%s/new", server.URL)
keyInfoBaseURL = fmt.Sprintf("%s", server.URL)
@ -74,10 +73,9 @@ func setup(sigServices signer.SigningServiceIndex) {
}
func TestDeleteKeyHandlerReturns404WithNonexistentKey(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
@ -95,14 +93,13 @@ func TestDeleteKeyHandlerReturns404WithNonexistentKey(t *testing.T) {
}
func TestDeleteKeyHandler(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
key, _ := sigService.CreateKey()
tufKey, _ := cryptoService.Create("", data.ED25519Key)
requestJson, _ := json.Marshal(key.KeyInfo.KeyID)
requestJson, _ := json.Marshal(&pb.KeyID{ID: tufKey.ID()})
reader = strings.NewReader(string(requestJson))
request, err := http.NewRequest("POST", deleteKeyBaseURL, reader)
@ -115,14 +112,13 @@ func TestDeleteKeyHandler(t *testing.T) {
}
func TestKeyInfoHandler(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
key, _ := sigService.CreateKey()
tufKey, _ := cryptoService.Create("", data.ED25519Key)
keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, key.KeyInfo.KeyID.ID)
keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, tufKey.ID())
request, err := http.NewRequest("GET", keyInfoURL, nil)
assert.Nil(t, err)
@ -137,15 +133,16 @@ func TestKeyInfoHandler(t *testing.T) {
err = json.Unmarshal(jsonBlob, &pubKey)
assert.Nil(t, err)
assert.Equal(t, key.KeyInfo.KeyID.ID, pubKey.KeyInfo.KeyID.ID)
assert.Equal(t, tufKey.ID(), pubKey.KeyInfo.KeyID.ID)
assert.Equal(t, 200, res.StatusCode)
}
func TestKeyInfoHandlerReturns404WithNonexistentKey(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
keyInfoURL := fmt.Sprintf("%s/%s", keyInfoBaseURL, fakeID)
@ -166,10 +163,8 @@ func TestHSMCreateKeyHandler(t *testing.T) {
defer ctx.CloseSession(session)
defer ctx.Logout(session)
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewRSASigningService(ctx, session)
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
cryptoService := api.NewRSAHardwareCryptoService(ctx, session)
setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService})
createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.RSAKey)
@ -190,10 +185,9 @@ func TestHSMCreateKeyHandler(t *testing.T) {
}
func TestSoftwareCreateKeyHandler(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
createKeyURL := fmt.Sprintf("%s/%s", createKeyBaseURL, data.ED25519Key)
@ -220,14 +214,12 @@ func TestHSMSignHandler(t *testing.T) {
defer ctx.CloseSession(session)
defer ctx.Logout(session)
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewRSASigningService(ctx, session)
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
cryptoService := api.NewRSAHardwareCryptoService(ctx, session)
setup(signer.CryptoServiceIndex{data.RSAKey: cryptoService})
key, _ := sigService.CreateKey()
tufKey, _ := cryptoService.Create("", data.RSAKey)
sigRequest := &pb.SignatureRequest{KeyID: key.KeyInfo.KeyID, Content: make([]byte, 10)}
sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: tufKey.ID()}, Content: make([]byte, 10)}
requestJson, _ := json.Marshal(sigRequest)
reader = strings.NewReader(string(requestJson))
@ -246,20 +238,19 @@ func TestHSMSignHandler(t *testing.T) {
err = json.Unmarshal(jsonBlob, &sig)
assert.Nil(t, err)
assert.Equal(t, key.KeyInfo.KeyID.ID, sig.KeyInfo.KeyID.ID)
assert.Equal(t, tufKey.ID, sig.KeyInfo.KeyID.ID)
assert.Equal(t, 200, res.StatusCode)
}
func TestSoftwareSignHandler(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
key, err := sigService.CreateKey()
tufKey, err := cryptoService.Create("", data.ED25519Key)
assert.Nil(t, err)
sigRequest := &pb.SignatureRequest{KeyID: key.KeyInfo.KeyID, Content: make([]byte, 10)}
sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: tufKey.ID()}, Content: make([]byte, 10)}
requestJson, _ := json.Marshal(sigRequest)
reader = strings.NewReader(string(requestJson))
@ -280,14 +271,13 @@ func TestSoftwareSignHandler(t *testing.T) {
err = json.Unmarshal(jsonBlob, &sig)
assert.Nil(t, err)
assert.Equal(t, key.KeyInfo.KeyID.ID, sig.KeyInfo.KeyID.ID)
assert.Equal(t, tufKey.ID(), sig.KeyInfo.KeyID.ID)
}
func TestSoftwareSignWithInvalidRequestHandler(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
requestJson := "{\"blob\":\"7d16f1d0b95310a7bc557747fc4f20fcd41c1c5095ae42f189df0717e7d7f4a0a2b55debce630f43c4ac099769c612965e3fda3cd4c0078ee6a460f14fa19307\"}"
reader = strings.NewReader(requestJson)
@ -309,14 +299,13 @@ func TestSoftwareSignWithInvalidRequestHandler(t *testing.T) {
}
func TestSignHandlerReturns404WithNonexistentKey(t *testing.T) {
// We associate both key types with this signing service to bypass the
// ID -> keyType logic in the tests
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
setup(signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService})
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
setup(signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService})
fakeID := "c62e6d68851cef1f7e55a9d56e3b0c05f3359f16838cad43600f0554e7d3b54d"
sigService.CreateKey()
cryptoService.Create("", data.ED25519Key)
sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: fakeID}, Content: make([]byte, 10)}
requestJson, _ := json.Marshal(sigRequest)

View File

@ -1,27 +0,0 @@
package api
import (
"github.com/agl/ed25519"
"github.com/endophage/gotuf/data"
pb "github.com/docker/notary/proto"
)
// Ed25519Signer implements the Signer interface for Ed25519 keys
type Ed25519Signer struct {
privateKey data.Key
}
// Sign returns a signature for a given blob
func (s *Ed25519Signer) Sign(request *pb.SignatureRequest) (*pb.Signature, error) {
priv := [ed25519.PrivateKeySize]byte{}
copy(priv[:], s.privateKey.Private())
sig := ed25519.Sign(&priv, request.Content)
return &pb.Signature{KeyInfo: &pb.KeyInfo{KeyID: &pb.KeyID{ID: s.privateKey.ID()}, Algorithm: &pb.Algorithm{Algorithm: data.ED25519Key.String()}}, Content: sig[:]}, nil
}
// NewEd25519Signer returns a Ed25519Signer, given a private key
func NewEd25519Signer(key data.Key) *Ed25519Signer {
return &Ed25519Signer{privateKey: key}
}

View File

@ -1,63 +0,0 @@
package api_test
import (
"encoding/hex"
"fmt"
"testing"
"github.com/agl/ed25519"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/signer/keys"
"github.com/endophage/gotuf/data"
"github.com/stretchr/testify/assert"
pb "github.com/docker/notary/proto"
)
type zeroReader struct{}
func (zeroReader) Read(buf []byte) (int, error) {
for i := range buf {
buf[i] = 0
}
return len(buf), nil
}
func TestSign(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519.GenerateKey(zero)
blob := []byte("test message")
directSig := ed25519.Sign(private, blob)
directSigHex := hex.EncodeToString(directSig[:])
key := data.NewPrivateKey(data.ED25519Key, public[:], private[:])
signer := api.NewEd25519Signer(key)
sigRequest := &pb.SignatureRequest{KeyID: &pb.KeyID{ID: key.ID()}, Content: blob}
sig, err := signer.Sign(sigRequest)
assert.Nil(t, err)
signatureHex := fmt.Sprintf("%x", sig.Content)
assert.Equal(t, directSigHex, signatureHex)
assert.Equal(t, sig.KeyInfo.KeyID.ID, key.ID())
}
func BenchmarkSign(b *testing.B) {
blob := []byte("7d16f1d0b95310a7bc557747fc4f20fcd41c1c5095ae42f189df")
keyDB := keys.NewKeyDB()
var sigService = api.NewEdDSASigningService(keyDB)
key, _ := sigService.CreateKey()
privkey, _ := keyDB.GetKey(key.KeyInfo.KeyID)
signer := api.NewEd25519Signer(privkey)
sigRequest := &pb.SignatureRequest{KeyID: key.KeyInfo.KeyID, Content: blob}
for n := 0; n < b.N; n++ {
_, _ = signer.Sign(sigRequest)
}
}

View File

@ -1,61 +0,0 @@
package api
import (
"crypto/rand"
"github.com/agl/ed25519"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/keys"
"github.com/endophage/gotuf/data"
pb "github.com/docker/notary/proto"
)
// EdDSASigningService is an implementation of SigningService
type EdDSASigningService struct {
KeyDB signer.KeyDatabase
}
// CreateKey creates a key and returns its public components
func (s EdDSASigningService) CreateKey() (*pb.PublicKey, error) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
k := data.NewPrivateKey(data.ED25519Key, pub[:], priv[:])
err = s.KeyDB.AddKey(k)
if err != nil {
return nil, err
}
pubKey := &pb.PublicKey{KeyInfo: &pb.KeyInfo{KeyID: &pb.KeyID{ID: k.ID()}, Algorithm: &pb.Algorithm{Algorithm: k.Algorithm().String()}}, PublicKey: pub[:]}
return pubKey, nil
}
// DeleteKey removes a key from the key database
func (s EdDSASigningService) DeleteKey(keyID *pb.KeyID) (*pb.Void, error) {
return s.KeyDB.DeleteKey(keyID)
}
// KeyInfo returns the public components of a particular key
func (s EdDSASigningService) KeyInfo(keyID *pb.KeyID) (*pb.PublicKey, error) {
return s.KeyDB.KeyInfo(keyID)
}
// Signer returns a Signer for a specific KeyID
func (s EdDSASigningService) Signer(keyID *pb.KeyID) (signer.Signer, error) {
key, err := s.KeyDB.GetKey(keyID)
if err != nil {
return nil, keys.ErrInvalidKeyID
}
return &Ed25519Signer{privateKey: key}, nil
}
// NewEdDSASigningService returns an instance of KeyDB
func NewEdDSASigningService(keyDB signer.KeyDatabase) *EdDSASigningService {
return &EdDSASigningService{
KeyDB: keyDB,
}
}

View File

@ -1,99 +0,0 @@
package api_test
import (
"testing"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/signer/keys"
"github.com/endophage/gotuf/data"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
pb "github.com/docker/notary/proto"
)
type FakeKeyDB struct {
mock.Mock
}
func (m *FakeKeyDB) CreateKey() (*pb.PublicKey, error) {
args := m.Mock.Called()
return args.Get(0).(*pb.PublicKey), args.Error(1)
}
func (m *FakeKeyDB) AddKey(key data.Key) error {
args := m.Mock.Called()
return args.Error(0)
}
func (m *FakeKeyDB) DeleteKey(keyID *pb.KeyID) (*pb.Void, error) {
args := m.Mock.Called(keyID.ID)
return nil, args.Error(0)
}
func (m *FakeKeyDB) KeyInfo(keyID *pb.KeyID) (*pb.PublicKey, error) {
args := m.Mock.Called(keyID.ID)
return args.Get(0).(*pb.PublicKey), args.Error(1)
}
func (m *FakeKeyDB) GetKey(keyID *pb.KeyID) (data.Key, error) {
args := m.Mock.Called(keyID.ID)
return args.Get(0).(data.Key), args.Error(1)
}
func TestDeleteKey(t *testing.T) {
fakeKeyID := "830158bb5a4af00a3f689a8f29120f0fa7f8ae57cf00ce1fede8ae8652b5181a"
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("DeleteKey", fakeKeyID).Return(nil).Once()
_, err := sigService.DeleteKey(&pb.KeyID{ID: fakeKeyID})
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func TestKeyInfo(t *testing.T) {
fakeKeyID := "830158bb5a4af00a3f689a8f29120f0fa7f8ae57cf00ce1fede8ae8652b5181a"
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("KeyInfo", fakeKeyID).Return(&pb.PublicKey{}, nil).Once()
_, err := sigService.KeyInfo(&pb.KeyID{ID: fakeKeyID})
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func TestCreateKey(t *testing.T) {
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("AddKey").Return(nil).Once()
_, err := sigService.CreateKey()
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func TestSigner(t *testing.T) {
fakeKeyID := "830158bb5a4af00a3f689a8f29120f0fa7f8ae57cf00ce1fede8ae8652b5181a"
m := FakeKeyDB{}
sigService := api.NewEdDSASigningService(&m)
m.On("GetKey", fakeKeyID).Return(&data.PrivateKey{}, nil).Once()
_, err := sigService.Signer(&pb.KeyID{ID: fakeKeyID})
m.Mock.AssertExpectations(t)
assert.Nil(t, err)
}
func BenchmarkCreateKey(b *testing.B) {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
for n := 0; n < b.N; n++ {
_, _ = sigService.CreateKey()
}
}

View File

@ -3,6 +3,8 @@ package api
import (
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/keys"
"github.com/endophage/gotuf/data"
"github.com/endophage/gotuf/signed"
pb "github.com/docker/notary/proto"
)
@ -10,10 +12,12 @@ import (
// FindKeyByID looks for the key with the given ID in each of the
// signing services in sigServices. It returns the first matching key it finds,
// or ErrInvalidKeyID if the key is not found in any of the signing services.
func FindKeyByID(sigServices signer.SigningServiceIndex, keyID *pb.KeyID) (*pb.PublicKey, signer.SigningService, error) {
for _, service := range sigServices {
key, err := service.KeyInfo(keyID)
if err == nil {
// It also returns the CryptoService associated with the key, so the caller
// can perform operations with the key (such as signing).
func FindKeyByID(cryptoServices signer.CryptoServiceIndex, keyID *pb.KeyID) (data.Key, signed.CryptoService, error) {
for _, service := range cryptoServices {
key := service.GetKey(keyID.ID)
if key != nil {
return key, service, nil
}
}

View File

@ -17,39 +17,47 @@ import (
//KeyManagementServer implements the KeyManagementServer grpc interface
type KeyManagementServer struct {
SigServices signer.SigningServiceIndex
CryptoServices signer.CryptoServiceIndex
}
//SignerServer implements the SignerServer grpc interface
type SignerServer struct {
SigServices signer.SigningServiceIndex
CryptoServices signer.CryptoServiceIndex
}
//CreateKey returns a PublicKey created using KeyManagementServer's SigningService
func (s *KeyManagementServer) CreateKey(ctx context.Context, algorithm *pb.Algorithm) (*pb.PublicKey, error) {
service := s.SigServices[data.KeyAlgorithm(algorithm.Algorithm)]
keyAlgo := data.KeyAlgorithm(algorithm.Algorithm)
service := s.CryptoServices[keyAlgo]
if service == nil {
return nil, fmt.Errorf("algorithm %s not supported for create key", algorithm.Algorithm)
}
key, err := service.CreateKey()
tufKey, err := service.Create("", keyAlgo)
if err != nil {
return nil, grpc.Errorf(codes.Internal, "Key creation failed")
}
log.Println("[Notary-signer CreateKey] : Created KeyID ", key.KeyInfo.KeyID.ID)
return key, nil
log.Println("[Notary-signer CreateKey] : Created KeyID ", tufKey.ID())
return &pb.PublicKey{
KeyInfo: &pb.KeyInfo{
KeyID: &pb.KeyID{ID: tufKey.ID()},
Algorithm: &pb.Algorithm{Algorithm: tufKey.Algorithm().String()},
},
PublicKey: tufKey.Public(),
}, nil
}
//DeleteKey deletes they key associated with a KeyID
func (s *KeyManagementServer) DeleteKey(ctx context.Context, keyID *pb.KeyID) (*pb.Void, error) {
_, service, err := FindKeyByID(s.SigServices, keyID)
_, service, err := FindKeyByID(s.CryptoServices, keyID)
if err != nil {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", keyID.ID)
}
_, err = service.DeleteKey(keyID)
err = service.RemoveKey(keyID.ID)
log.Println("[Notary-signer DeleteKey] : Deleted KeyID ", keyID.ID)
if err != nil {
switch err {
@ -65,39 +73,47 @@ func (s *KeyManagementServer) DeleteKey(ctx context.Context, keyID *pb.KeyID) (*
//GetKeyInfo returns they PublicKey associated with a KeyID
func (s *KeyManagementServer) GetKeyInfo(ctx context.Context, keyID *pb.KeyID) (*pb.PublicKey, error) {
_, service, err := FindKeyByID(s.SigServices, keyID)
_, service, err := FindKeyByID(s.CryptoServices, keyID)
if err != nil {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", keyID.ID)
}
key, err := service.KeyInfo(keyID)
if err != nil {
tufKey := service.GetKey(keyID.ID)
if tufKey == nil {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", keyID.ID)
}
log.Println("[Notary-signer GetKeyInfo] : Returning PublicKey for KeyID ", keyID.ID)
return key, nil
return &pb.PublicKey{
KeyInfo: &pb.KeyInfo{
KeyID: &pb.KeyID{ID: tufKey.ID()},
Algorithm: &pb.Algorithm{Algorithm: tufKey.Algorithm().String()},
},
PublicKey: tufKey.Public(),
}, nil
}
//Sign signs a message and returns the signature using a private key associate with the KeyID from the SignatureRequest
func (s *SignerServer) Sign(ctx context.Context, sr *pb.SignatureRequest) (*pb.Signature, error) {
_, service, err := FindKeyByID(s.SigServices, sr.KeyID)
tufKey, service, err := FindKeyByID(s.CryptoServices, sr.KeyID)
if err != nil {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", sr.KeyID.ID)
}
log.Println("[Notary-signer Sign] : Signing ", string(sr.Content), " with KeyID ", sr.KeyID.ID)
signer, err := service.Signer(sr.KeyID)
if err == keys.ErrInvalidKeyID {
return nil, grpc.Errorf(codes.NotFound, "Invalid keyID: key %s not found", sr.KeyID.ID)
} else if err != nil {
signatures, err := service.Sign([]string{sr.KeyID.ID}, sr.Content)
if err != nil || len(signatures) != 1 {
return nil, grpc.Errorf(codes.Internal, "Signing failed for keyID %s on hash %s", sr.KeyID.ID, sr.Content)
}
signature, err := signer.Sign(sr)
if err != nil {
return nil, grpc.Errorf(codes.Internal, "Signing failed for keyID %s on hash %s", sr.KeyID.ID, sr.Content)
signature := &pb.Signature{
KeyInfo: &pb.KeyInfo{
KeyID: &pb.KeyID{ID: tufKey.ID()},
Algorithm: &pb.Algorithm{Algorithm: tufKey.Algorithm().String()},
},
Content: signatures[0].Signature,
}
return signature, nil

View File

@ -6,9 +6,10 @@ import (
"net"
"testing"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/signer/keys"
"github.com/docker/notary/trustmanager"
"github.com/endophage/gotuf/data"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
@ -26,12 +27,13 @@ var (
)
func init() {
sigService := api.NewEdDSASigningService(keys.NewKeyDB())
sigServices := signer.SigningServiceIndex{data.ED25519Key: sigService, data.RSAKey: sigService}
keyStore := trustmanager.NewKeyMemoryStore()
cryptoService := cryptoservice.NewCryptoService("", keyStore, "")
cryptoServices := signer.CryptoServiceIndex{data.ED25519Key: cryptoService, data.RSAKey: cryptoService, data.ECDSAKey: cryptoService}
void = &pb.Void{}
//server setup
kms := &api.KeyManagementServer{SigServices: sigServices}
ss := &api.SignerServer{SigServices: sigServices}
kms := &api.KeyManagementServer{CryptoServices: cryptoServices}
ss := &api.SignerServer{CryptoServices: cryptoServices}
grpcServer = grpc.NewServer()
pb.RegisterKeyManagementServer(grpcServer, kms)
pb.RegisterSignerServer(grpcServer, ss)

View File

@ -10,23 +10,20 @@ import (
"log"
"math/big"
"github.com/docker/notary/signer"
"github.com/docker/notary/signer/keys"
"github.com/endophage/gotuf/data"
"github.com/miekg/pkcs11"
pb "github.com/docker/notary/proto"
)
// RSASigningService is an implementation of SigningService
type RSASigningService struct {
// RSAHardwareCryptoService is an implementation of SigningService
type RSAHardwareCryptoService struct {
keys map[string]*keys.HSMRSAKey
context *pkcs11.Ctx
session pkcs11.SessionHandle
}
// CreateKey creates a key and returns its public components
func (s RSASigningService) CreateKey() (*pb.PublicKey, error) {
// Create creates a key and returns its public components
func (s *RSAHardwareCryptoService) Create(role string, algo data.KeyAlgorithm) (data.Key, error) {
// For now generate random labels for keys
// (diogo): add link between keyID and label in database so we can support multiple keys
randomLabel := make([]byte, 32)
@ -101,100 +98,87 @@ func (s RSASigningService) CreateKey() (*pb.PublicKey, error) {
s.keys[keyID] = k
pubKey := &pb.PublicKey{KeyInfo: &pb.KeyInfo{KeyID: &pb.KeyID{ID: keyID}, Algorithm: &pb.Algorithm{Algorithm: k.Algorithm().String()}}, PublicKey: k.Public()}
return pubKey, nil
return k, nil
}
// DeleteKey removes a key from the key database
func (s RSASigningService) DeleteKey(keyID *pb.KeyID) (*pb.Void, error) {
if _, ok := s.keys[keyID.ID]; !ok {
return nil, keys.ErrInvalidKeyID
// RemoveKey removes a key from the key database
func (s *RSAHardwareCryptoService) RemoveKey(keyID string) error {
if _, ok := s.keys[keyID]; !ok {
return keys.ErrInvalidKeyID
}
delete(s.keys, keyID.ID)
return nil, nil
delete(s.keys, keyID)
return nil
}
// KeyInfo returns the public components of a particular key
func (s RSASigningService) KeyInfo(keyID *pb.KeyID) (*pb.PublicKey, error) {
k, ok := s.keys[keyID.ID]
if !ok {
return nil, keys.ErrInvalidKeyID
}
pubKey := &pb.PublicKey{KeyInfo: &pb.KeyInfo{KeyID: keyID, Algorithm: &pb.Algorithm{Algorithm: k.Algorithm().String()}}, PublicKey: k.Public()}
return pubKey, nil
}
// Signer returns a Signer for a specific KeyID
func (s RSASigningService) Signer(keyID *pb.KeyID) (signer.Signer, error) {
key, ok := s.keys[keyID.ID]
if !ok {
return nil, keys.ErrInvalidKeyID
}
// TODO(diogo): Investigate if caching is worth it. Is this object expensive to create?
return &RSASigner{privateKey: key, context: s.context, session: s.session}, nil
}
// RSASigner implements the Signer interface for RSA keys
type RSASigner struct {
privateKey *keys.HSMRSAKey
context *pkcs11.Ctx
session pkcs11.SessionHandle
// GetKey returns the public components of a particular key
func (s *RSAHardwareCryptoService) GetKey(keyID string) data.Key {
return s.keys[keyID]
}
// Sign returns a signature for a given signature request
func (s *RSASigner) Sign(request *pb.SignatureRequest) (*pb.Signature, error) {
priv := s.privateKey.PKCS11ObjectHandle()
var sig []byte
var err error
for i := 0; i < 3; i++ {
//TODO(mccauley): move this to RSA OAEP
s.context.SignInit(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA256_RSA_PKCS, nil)}, priv)
sig, err = s.context.Sign(s.session, request.Content)
if err != nil {
log.Printf("Error while signing: %s", err)
func (s *RSAHardwareCryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
signatures := make([]data.Signature, 0, len(keyIDs))
for _, keyid := range keyIDs {
privateKey, present := s.keys[keyid]
if !present {
// We skip keys that aren't found
continue
}
// (diogo): XXX: Remove this before shipping
digest := sha256.Sum256(request.Content)
pub, err := x509.ParsePKIXPublicKey(s.privateKey.Public())
if err != nil {
log.Printf("Failed to parse public key: %s\n", err)
return nil, err
priv := privateKey.PKCS11ObjectHandle()
var sig []byte
var err error
for i := 0; i < 3; i++ {
//TODO(mccauley): move this to RSA OAEP
s.context.SignInit(s.session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA256_RSA_PKCS, nil)}, priv)
sig, err = s.context.Sign(s.session, payload)
if err != nil {
log.Printf("Error while signing: %s", err)
continue
}
// (diogo): XXX: Remove this before shipping
digest := sha256.Sum256(payload)
pub, err := x509.ParsePKIXPublicKey(privateKey.Public())
if err != nil {
log.Printf("Failed to parse public key: %s\n", err)
return nil, err
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
log.Printf("Value returned from ParsePKIXPublicKey was not an RSA public key")
return nil, err
}
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig)
if err != nil {
log.Printf("Failed verification. Retrying: %s", err)
continue
}
break
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
log.Printf("Value returned from ParsePKIXPublicKey was not an RSA public key")
return nil, err
// (diogo): XXX: END Area of removal
if sig == nil {
return nil, errors.New("Failed to create signature")
}
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA256, digest[:], sig)
if err != nil {
log.Printf("Failed verification. Retrying: %s", err)
continue
}
break
signatures = append(signatures, data.Signature{
KeyID: keyid,
Method: data.RSAPKCS1v15Signature,
Signature: sig[:],
})
}
// (diogo): XXX: END Area of removal
if sig == nil {
return nil, errors.New("Failed to create signature")
}
returnSig := &pb.Signature{KeyInfo: &pb.KeyInfo{KeyID: &pb.KeyID{ID: s.privateKey.ID()}, Algorithm: &pb.Algorithm{Algorithm: s.privateKey.Algorithm().String()}}, Content: sig[:]}
log.Printf("[Notary-signer Server] Signature request JSON: %s , response: %s", string(request.Content), returnSig)
return returnSig, nil
return signatures, nil
}
// NewRSASigningService returns an instance of KeyDB
func NewRSASigningService(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) *RSASigningService {
return &RSASigningService{
// NewRSAHardwareCryptoService returns an instance of RSAHardwareCryptoService
func NewRSAHardwareCryptoService(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) *RSAHardwareCryptoService {
return &RSAHardwareCryptoService{
keys: make(map[string]*keys.HSMRSAKey),
context: ctx,
session: session,

View File

@ -1,62 +0,0 @@
package keys
import (
pb "github.com/docker/notary/proto"
"github.com/endophage/gotuf/data"
)
// KeyDB represents an in-memory key keystore
type KeyDB struct {
keys map[string]data.Key
}
// CreateKey is needed to implement KeyManager. Returns an empty key.
func (db *KeyDB) CreateKey() (*pb.PublicKey, error) {
k := &pb.PublicKey{}
return k, nil
}
// AddKey Adds a new key to the database
func (db *KeyDB) AddKey(key data.Key) error {
if _, ok := db.keys[key.ID()]; ok {
return ErrExists
}
db.keys[key.ID()] = key
return nil
}
// GetKey returns the private bits of a key
func (db *KeyDB) GetKey(keyID *pb.KeyID) (data.Key, error) {
if key, ok := db.keys[keyID.ID]; ok {
return key, nil
}
return nil, ErrInvalidKeyID
}
// DeleteKey deletes the keyID from the database
func (db *KeyDB) DeleteKey(keyID *pb.KeyID) (*pb.Void, error) {
_, err := db.GetKey(keyID)
if err != nil {
return nil, err
}
delete(db.keys, keyID.ID)
return nil, nil
}
// KeyInfo returns the public bits of a key, given a specific keyID
func (db *KeyDB) KeyInfo(keyID *pb.KeyID) (*pb.PublicKey, error) {
key, err := db.GetKey(keyID)
if err != nil {
return nil, err
}
return &pb.PublicKey{KeyInfo: &pb.KeyInfo{KeyID: keyID, Algorithm: &pb.Algorithm{Algorithm: key.Algorithm().String()}}, PublicKey: key.Public()}, nil
}
// NewKeyDB returns an instance of KeyDB
func NewKeyDB() *KeyDB {
return &KeyDB{
keys: make(map[string]data.Key),
}
}

View File

@ -3,6 +3,7 @@ package signer
import (
pb "github.com/docker/notary/proto"
"github.com/endophage/gotuf/data"
"github.com/endophage/gotuf/signed"
)
// SigningService is the interface to implement a key management and signing service
@ -13,9 +14,9 @@ type SigningService interface {
Signer(keyID *pb.KeyID) (Signer, error)
}
// SigningServiceIndex represents a mapping between a service algorithm string
// and a signing service
type SigningServiceIndex map[data.KeyAlgorithm]SigningService
// CryptoServiceIndex represents a mapping between a service algorithm string
// and a CryptoService
type CryptoServiceIndex map[data.KeyAlgorithm]signed.CryptoService
// KeyManager is the interface to implement key management (possibly a key database)
type KeyManager interface {

View File

@ -1,6 +1,7 @@
package signer
import (
"errors"
"net"
"github.com/Sirupsen/logrus"
@ -62,8 +63,7 @@ func (trust *NotarySigner) Sign(keyIDs []string, toSign []byte) ([]data.Signatur
}
// Create creates a remote key and returns the PublicKey associated with the remote private key
// TODO(diogo): Ignoring algorithm for now until notary-signer supports it
func (trust *NotarySigner) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) {
func (trust *NotarySigner) Create(role string, algorithm data.KeyAlgorithm) (data.Key, error) {
publicKey, err := trust.kmClient.CreateKey(context.Background(), &pb.Algorithm{Algorithm: algorithm.String()})
if err != nil {
return nil, err
@ -73,17 +73,14 @@ func (trust *NotarySigner) Create(role string, algorithm data.KeyAlgorithm) (*da
return public, nil
}
// PublicKeys returns the public key(s) associated with the passed in keyIDs
func (trust *NotarySigner) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
publicKeys := make(map[string]*data.PublicKey)
for _, ID := range keyIDs {
keyID := pb.KeyID{ID: ID}
public, err := trust.kmClient.GetKeyInfo(context.Background(), &keyID)
if err != nil {
return nil, err
}
publicKeys[public.KeyInfo.KeyID.ID] =
data.NewPublicKey(data.KeyAlgorithm(public.KeyInfo.Algorithm.Algorithm), public.PublicKey)
}
return publicKeys, nil
// RemoveKey deletes a key
func (trust *NotarySigner) RemoveKey(keyid string) error {
//TODO(aaronl): Not implemented yet
return errors.New("DeleteKey not implemented in NotarySigner")
}
// GetKey retrieves a key
func (trust *NotarySigner) GetKey(keyid string) data.Key {
//TODO(aaronl): Not implemented yet
return nil
}

View File

@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
)
const (
@ -20,14 +21,21 @@ var (
ErrPathOutsideStore = errors.New("path outside file store")
)
// FileStore is the interface for all FileStores
type FileStore interface {
// LimitedFileStore implements the bare bones primitives (no symlinks or
// hierarchy)
type LimitedFileStore interface {
Add(fileName string, data []byte) error
Remove(fileName string) error
RemoveDir(directoryName string) error
Get(fileName string) ([]byte, error)
GetPath(fileName string) (string, error)
ListFiles(symlinks bool) []string
}
// FileStore is the interface for full-featured FileStores
type FileStore interface {
LimitedFileStore
RemoveDir(directoryName string) error
GetPath(fileName string) (string, error)
ListDir(directoryName string, symlinks bool) []string
Link(src, dst string) error
BaseDir() string
@ -41,7 +49,7 @@ type SimpleFileStore struct {
}
// NewSimpleFileStore creates a directory with 755 permissions
func NewSimpleFileStore(baseDir string, fileExt string) (FileStore, error) {
func NewSimpleFileStore(baseDir string, fileExt string) (*SimpleFileStore, error) {
baseDir = filepath.Clean(baseDir)
if err := CreateDirectory(baseDir); err != nil {
@ -56,7 +64,7 @@ func NewSimpleFileStore(baseDir string, fileExt string) (FileStore, error) {
}
// NewPrivateSimpleFileStore creates a directory with 700 permissions
func NewPrivateSimpleFileStore(baseDir string, fileExt string) (FileStore, error) {
func NewPrivateSimpleFileStore(baseDir string, fileExt string) (*SimpleFileStore, error) {
if err := CreatePrivateDirectory(baseDir); err != nil {
return nil, err
}
@ -213,3 +221,68 @@ func createDirectory(dir string, perms os.FileMode) error {
dir = dir + "/"
return os.MkdirAll(dir, perms)
}
// MemoryFileStore is an implementation of LimitedFileStore that keeps
// the contents in memory.
type MemoryFileStore struct {
sync.Mutex
files map[string][]byte
}
// NewMemoryFileStore creates a MemoryFileStore
func NewMemoryFileStore() *MemoryFileStore {
return &MemoryFileStore{
files: make(map[string][]byte),
}
}
// ErrMemFileNotFound is returned for a nonexistent "file" in the memory file
// store
var ErrMemFileNotFound = errors.New("key not found in memory file store")
// Add writes data to a file with a given name
func (f *MemoryFileStore) Add(name string, data []byte) error {
f.Lock()
defer f.Unlock()
f.files[name] = data
return nil
}
// Remove removes a file identified by name
func (f *MemoryFileStore) Remove(name string) error {
f.Lock()
defer f.Unlock()
if _, present := f.files[name]; !present {
return ErrMemFileNotFound
}
delete(f.files, name)
return nil
}
// Get returns the data given a file name
func (f *MemoryFileStore) Get(name string) ([]byte, error) {
f.Lock()
defer f.Unlock()
fileData, present := f.files[name]
if !present {
return nil, ErrMemFileNotFound
}
return fileData, nil
}
// ListFiles lists all the files inside of a store
func (f *MemoryFileStore) ListFiles(symlinks bool) []string {
var list []string
for name := range f.files {
list = append(list, name)
}
return list
}

View File

@ -11,9 +11,25 @@ const (
keyExtension = "key"
)
// KeyStore is a generic interface for private key storage
type KeyStore interface {
LimitedFileStore
AddKey(name string, privKey *data.PrivateKey) error
GetKey(name string) (*data.PrivateKey, error)
AddEncryptedKey(name string, privKey *data.PrivateKey, passphrase string) error
GetDecryptedKey(name string, passphrase string) (*data.PrivateKey, error)
ListKeys() []string
}
// KeyFileStore persists and manages private keys on disk
type KeyFileStore struct {
FileStore
SimpleFileStore
}
// KeyMemoryStore manages private keys in memory
type KeyMemoryStore struct {
MemoryFileStore
}
// NewKeyFileStore returns a new KeyFileStore creating a private directory to
@ -24,11 +40,73 @@ func NewKeyFileStore(baseDir string) (*KeyFileStore, error) {
return nil, err
}
return &KeyFileStore{fileStore}, nil
return &KeyFileStore{*fileStore}, nil
}
// AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyFileStore) AddKey(name string, privKey *data.PrivateKey) error {
return addKey(s, name, privKey)
}
// GetKey returns the PrivateKey given a KeyID
func (s *KeyFileStore) GetKey(name string) (*data.PrivateKey, error) {
return getKey(s, name)
}
// AddEncryptedKey stores the contents of a PEM-encoded private key as an encrypted PEM block
func (s *KeyFileStore) AddEncryptedKey(name string, privKey *data.PrivateKey, passphrase string) error {
return addEncryptedKey(s, name, privKey, passphrase)
}
// GetDecryptedKey decrypts and returns the PEM Encoded private key given a flename
// and a passphrase
func (s *KeyFileStore) GetDecryptedKey(name string, passphrase string) (*data.PrivateKey, error) {
return getDecryptedKey(s, name, passphrase)
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
// There might be symlinks associating Certificate IDs to Public Keys, so this
// method only returns the IDs that aren't symlinks
func (s *KeyFileStore) ListKeys() []string {
return listKeys(s)
}
// NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
func NewKeyMemoryStore() *KeyMemoryStore {
memStore := NewMemoryFileStore()
return &KeyMemoryStore{*memStore}
}
// AddKey stores the contents of a PEM-encoded private key as a PEM block
func (s *KeyMemoryStore) AddKey(name string, privKey *data.PrivateKey) error {
return addKey(s, name, privKey)
}
// GetKey returns the PrivateKey given a KeyID
func (s *KeyMemoryStore) GetKey(name string) (*data.PrivateKey, error) {
return getKey(s, name)
}
// AddEncryptedKey stores the contents of a PEM-encoded private key as an encrypted PEM block
func (s *KeyMemoryStore) AddEncryptedKey(name string, privKey *data.PrivateKey, passphrase string) error {
return addEncryptedKey(s, name, privKey, passphrase)
}
// GetDecryptedKey decrypts and returns the PEM Encoded private key given a flename
// and a passphrase
func (s *KeyMemoryStore) GetDecryptedKey(name string, passphrase string) (*data.PrivateKey, error) {
return getDecryptedKey(s, name, passphrase)
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
// There might be symlinks associating Certificate IDs to Public Keys, so this
// method only returns the IDs that aren't symlinks
func (s *KeyMemoryStore) ListKeys() []string {
return listKeys(s)
}
func addKey(s LimitedFileStore, name string, privKey *data.PrivateKey) error {
pemPrivKey, err := KeyToPEM(privKey)
if err != nil {
return err
@ -37,8 +115,7 @@ func (s *KeyFileStore) AddKey(name string, privKey *data.PrivateKey) error {
return s.Add(name, pemPrivKey)
}
// GetKey returns the PrivateKey given a KeyID
func (s *KeyFileStore) GetKey(name string) (*data.PrivateKey, error) {
func getKey(s LimitedFileStore, name string) (*data.PrivateKey, error) {
keyBytes, err := s.Get(name)
if err != nil {
return nil, err
@ -53,8 +130,7 @@ func (s *KeyFileStore) GetKey(name string) (*data.PrivateKey, error) {
return privKey, nil
}
// AddEncryptedKey stores the contents of a PEM-encoded private key as an encrypted PEM block
func (s *KeyFileStore) AddEncryptedKey(name string, privKey *data.PrivateKey, passphrase string) error {
func addEncryptedKey(s LimitedFileStore, name string, privKey *data.PrivateKey, passphrase string) error {
encryptedPrivKey, err := EncryptPrivateKey(privKey, passphrase)
if err != nil {
return err
@ -63,9 +139,7 @@ func (s *KeyFileStore) AddEncryptedKey(name string, privKey *data.PrivateKey, pa
return s.Add(name, encryptedPrivKey)
}
// GetDecryptedKey decrypts and returns the PEM Encoded private key given a flename
// and a passphrase
func (s *KeyFileStore) GetDecryptedKey(name string, passphrase string) (*data.PrivateKey, error) {
func getDecryptedKey(s LimitedFileStore, name string, passphrase string) (*data.PrivateKey, error) {
keyBytes, err := s.Get(name)
if err != nil {
return nil, err
@ -80,10 +154,7 @@ func (s *KeyFileStore) GetDecryptedKey(name string, passphrase string) (*data.Pr
return privKey, nil
}
// ListKeys returns a list of unique PublicKeys present on the KeyFileStore.
// There might be symlinks associating Certificate IDs to Public Keys, so this
// method only returns the IDs that aren't symlinks
func (s *KeyFileStore) ListKeys() []string {
func listKeys(s LimitedFileStore) []string {
var keyIDList []string
for _, f := range s.ListFiles(false) {
keyID := strings.TrimSpace(strings.TrimSuffix(filepath.Base(f), filepath.Ext(f)))