mirror of https://github.com/docker/docs.git
204 lines
5.6 KiB
Go
204 lines
5.6 KiB
Go
package trustmanager
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/docker/notary/pkg/passphrase"
|
|
jose "github.com/dvsekhvalnov/jose2go"
|
|
"github.com/endophage/gotuf/data"
|
|
"github.com/jinzhu/gorm"
|
|
)
|
|
|
|
// Constants
|
|
const (
|
|
EncryptionAlg = jose.A256GCM
|
|
KeywrapAlg = jose.PBES2_HS256_A128KW
|
|
)
|
|
|
|
// KeyDBStore persists and manages private keys on a SQL database
|
|
type KeyDBStore struct {
|
|
sync.Mutex
|
|
db gorm.DB
|
|
defaultPassAlias string
|
|
retriever passphrase.Retriever
|
|
cachedKeys map[string]data.PrivateKey
|
|
}
|
|
|
|
// GormPrivateKey represents a PrivateKey in the database
|
|
type GormPrivateKey struct {
|
|
gorm.Model
|
|
KeyID string `sql:"not null;unique;index:key_id_idx"`
|
|
EncryptionAlg string `sql:"not null"`
|
|
KeywrapAlg string `sql:"not null"`
|
|
Algorithm string `sql:"not null"`
|
|
PassphraseAlias string `sql:"not null"`
|
|
Public string `sql:"not null"`
|
|
Private string `sql:"not null"`
|
|
}
|
|
|
|
// TableName sets a specific table name for our GormPrivateKey
|
|
func (g GormPrivateKey) TableName() string {
|
|
return "private_keys"
|
|
}
|
|
|
|
// NewKeyDBStore returns a new KeyDBStore backed by a SQL database
|
|
func NewKeyDBStore(passphraseRetriever passphrase.Retriever, defaultPassAlias, dbType string, dbSQL *sql.DB) (*KeyDBStore, error) {
|
|
cachedKeys := make(map[string]data.PrivateKey)
|
|
|
|
// Open a connection to our database
|
|
db, _ := gorm.Open(dbType, dbSQL)
|
|
|
|
return &KeyDBStore{db: db,
|
|
defaultPassAlias: defaultPassAlias,
|
|
retriever: passphraseRetriever,
|
|
cachedKeys: cachedKeys}, nil
|
|
}
|
|
|
|
// AddKey stores the contents of a private key. Both name and alias are ignored,
|
|
// we always use Key IDs as name, and don't support aliases
|
|
func (s *KeyDBStore) AddKey(name, alias string, privKey data.PrivateKey) error {
|
|
|
|
passphrase, _, err := s.retriever(privKey.ID(), s.defaultPassAlias, false, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
encryptedKey, err := jose.Encrypt(string(privKey.Private()), KeywrapAlg, EncryptionAlg, passphrase)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
gormPrivKey := GormPrivateKey{
|
|
KeyID: privKey.ID(),
|
|
EncryptionAlg: EncryptionAlg,
|
|
KeywrapAlg: KeywrapAlg,
|
|
PassphraseAlias: s.defaultPassAlias,
|
|
Algorithm: privKey.Algorithm().String(),
|
|
Public: string(privKey.Public()),
|
|
Private: encryptedKey}
|
|
|
|
// Add encrypted private key to the database
|
|
s.db.Create(&gormPrivKey)
|
|
// Value will be false if Create suceeds
|
|
failure := s.db.NewRecord(gormPrivKey)
|
|
if failure {
|
|
return fmt.Errorf("failed to add private key to database: %s", privKey.ID())
|
|
}
|
|
|
|
// Add the private key to our cache
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
s.cachedKeys[privKey.ID()] = privKey
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetKey returns the PrivateKey given a KeyID
|
|
func (s *KeyDBStore) GetKey(name string) (data.PrivateKey, string, error) {
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
cachedKeyEntry, ok := s.cachedKeys[name]
|
|
if ok {
|
|
return cachedKeyEntry, "", nil
|
|
}
|
|
|
|
// Retrieve the GORM private key from the database
|
|
dbPrivateKey := GormPrivateKey{}
|
|
if s.db.Where(&GormPrivateKey{KeyID: name}).First(&dbPrivateKey).RecordNotFound() {
|
|
return nil, "", ErrKeyNotFound{}
|
|
}
|
|
|
|
// Get the passphrase to use for this key
|
|
passphrase, _, err := s.retriever(dbPrivateKey.KeyID, dbPrivateKey.PassphraseAlias, false, 1)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
// Decrypt private bytes from the gorm key
|
|
decryptedPrivKey, _, err := jose.Decode(dbPrivateKey.Private, passphrase)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
// Create a new PrivateKey with unencrypted bytes
|
|
privKey := data.NewPrivateKey(data.KeyAlgorithm(dbPrivateKey.Algorithm), []byte(dbPrivateKey.Public), []byte(decryptedPrivKey))
|
|
|
|
// Add the key to cache
|
|
s.cachedKeys[privKey.ID()] = privKey
|
|
|
|
return privKey, "", nil
|
|
}
|
|
|
|
// ListKeys always returns nil. This method is here to satisfy the KeyStore interface
|
|
func (s *KeyDBStore) ListKeys() []string {
|
|
return nil
|
|
}
|
|
|
|
// RemoveKey removes the key from the keyfilestore
|
|
func (s *KeyDBStore) RemoveKey(name string) error {
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
delete(s.cachedKeys, name)
|
|
|
|
// Retrieve the GORM private key from the database
|
|
dbPrivateKey := GormPrivateKey{}
|
|
if s.db.Where(&GormPrivateKey{KeyID: name}).First(&dbPrivateKey).RecordNotFound() {
|
|
return ErrKeyNotFound{}
|
|
}
|
|
|
|
// Delete the key from the database
|
|
s.db.Delete(&dbPrivateKey)
|
|
|
|
return nil
|
|
}
|
|
|
|
// RotateKeyPassphrase rotates the key-encryption-key
|
|
func (s *KeyDBStore) RotateKeyPassphrase(name, newPassphraseAlias string) error {
|
|
// Retrieve the GORM private key from the database
|
|
dbPrivateKey := GormPrivateKey{}
|
|
if s.db.Where(&GormPrivateKey{KeyID: name}).First(&dbPrivateKey).RecordNotFound() {
|
|
return ErrKeyNotFound{}
|
|
}
|
|
|
|
// Get the current passphrase to use for this key
|
|
passphrase, _, err := s.retriever(dbPrivateKey.KeyID, dbPrivateKey.PassphraseAlias, false, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println("Got old passphrase: ", passphrase)
|
|
|
|
// Decrypt private bytes from the gorm key
|
|
decryptedPrivKey, _, err := jose.Decode(dbPrivateKey.Private, passphrase)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get the new passphrase to use for this key
|
|
newPassphrase, _, err := s.retriever(dbPrivateKey.KeyID, newPassphraseAlias, false, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println("new passphrase: ", newPassphrase)
|
|
|
|
// Re-encrypt the private bytes with the new passphrase
|
|
newEncryptedKey, err := jose.Encrypt(decryptedPrivKey, KeywrapAlg, EncryptionAlg, newPassphrase)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("encrypted key: ", newEncryptedKey)
|
|
|
|
// Update the database object
|
|
dbPrivateKey.Private = newEncryptedKey
|
|
dbPrivateKey.PassphraseAlias = newPassphraseAlias
|
|
s.db.Save(dbPrivateKey)
|
|
|
|
fmt.Printf("DB Private key: %v", dbPrivateKey)
|
|
|
|
return nil
|
|
}
|