forked from mystiq/dex
pkg/crypto: replace old crypto with new crypto
This commit is contained in:
parent
8d6474b5fd
commit
07a4d4441e
8 changed files with 43 additions and 18 deletions
|
@ -32,6 +32,8 @@ func main() {
|
||||||
keySecrets := pflag.NewBase64List(32)
|
keySecrets := pflag.NewBase64List(32)
|
||||||
fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")
|
fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")
|
||||||
|
|
||||||
|
useOldFormat := fs.Bool("use-deprecated-secret-format", false, "In prior releases, the database used AES-CBC to encrypt keys. New deployments should use the default AES-GCM encryption.")
|
||||||
|
|
||||||
dbURL := fs.String("db-url", "", "DSN-formatted database connection string")
|
dbURL := fs.String("db-url", "", "DSN-formatted database connection string")
|
||||||
|
|
||||||
dbMigrate := fs.Bool("db-migrate", true, "perform database migrations when starting up overlord. This includes the initial DB objects creation.")
|
dbMigrate := fs.Bool("db-migrate", true, "perform database migrations when starting up overlord. This includes the initial DB objects creation.")
|
||||||
|
@ -100,7 +102,7 @@ func main() {
|
||||||
userManager := user.NewManager(userRepo,
|
userManager := user.NewManager(userRepo,
|
||||||
pwiRepo, db.TransactionFactory(dbc), user.ManagerOptions{})
|
pwiRepo, db.TransactionFactory(dbc), user.ManagerOptions{})
|
||||||
adminAPI := admin.NewAdminAPI(userManager, userRepo, pwiRepo, *localConnectorID)
|
adminAPI := admin.NewAdminAPI(userManager, userRepo, pwiRepo, *localConnectorID)
|
||||||
kRepo, err := db.NewPrivateKeySetRepo(dbc, keySecrets.BytesSlice()...)
|
kRepo, err := db.NewPrivateKeySetRepo(dbc, *useOldFormat, keySecrets.BytesSlice()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,8 @@ func main() {
|
||||||
keySecrets := pflag.NewBase64List(32)
|
keySecrets := pflag.NewBase64List(32)
|
||||||
fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")
|
fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")
|
||||||
|
|
||||||
|
useOldFormat := fs.Bool("use-deprecated-secret-format", false, "In prior releases, the database used AES-CBC to encrypt keys. New deployments should use the default AES-GCM encryption.")
|
||||||
|
|
||||||
dbMaxIdleConns := fs.Int("db-max-idle-conns", 0, "maximum number of connections in the idle connection pool")
|
dbMaxIdleConns := fs.Int("db-max-idle-conns", 0, "maximum number of connections in the idle connection pool")
|
||||||
dbMaxOpenConns := fs.Int("db-max-open-conns", 0, "maximum number of open connections to the database")
|
dbMaxOpenConns := fs.Int("db-max-open-conns", 0, "maximum number of open connections to the database")
|
||||||
|
|
||||||
|
@ -147,6 +149,7 @@ func main() {
|
||||||
scfg.StateConfig = &server.MultiServerConfig{
|
scfg.StateConfig = &server.MultiServerConfig{
|
||||||
KeySecrets: keySecrets.BytesSlice(),
|
KeySecrets: keySecrets.BytesSlice(),
|
||||||
DatabaseConfig: dbCfg,
|
DatabaseConfig: dbCfg,
|
||||||
|
UseOldFormat: *useOldFormat,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
db/key.go
29
db/key.go
|
@ -89,7 +89,7 @@ type privateKeySetBlob struct {
|
||||||
Value []byte `db:"value"`
|
Value []byte `db:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrivateKeySetRepo(dbm *gorp.DbMap, secrets ...[]byte) (*PrivateKeySetRepo, error) {
|
func NewPrivateKeySetRepo(dbm *gorp.DbMap, useOldFormat bool, secrets ...[]byte) (*PrivateKeySetRepo, error) {
|
||||||
for i, secret := range secrets {
|
for i, secret := range secrets {
|
||||||
if len(secret) != 32 {
|
if len(secret) != 32 {
|
||||||
return nil, fmt.Errorf("key secret %d: expected 32-byte secret", i)
|
return nil, fmt.Errorf("key secret %d: expected 32-byte secret", i)
|
||||||
|
@ -97,16 +97,18 @@ func NewPrivateKeySetRepo(dbm *gorp.DbMap, secrets ...[]byte) (*PrivateKeySetRep
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &PrivateKeySetRepo{
|
r := &PrivateKeySetRepo{
|
||||||
dbMap: dbm,
|
dbMap: dbm,
|
||||||
secrets: secrets,
|
useOldFormat: useOldFormat,
|
||||||
|
secrets: secrets,
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type PrivateKeySetRepo struct {
|
type PrivateKeySetRepo struct {
|
||||||
dbMap *gorp.DbMap
|
dbMap *gorp.DbMap
|
||||||
secrets [][]byte
|
useOldFormat bool
|
||||||
|
secrets [][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PrivateKeySetRepo) Set(ks key.KeySet) error {
|
func (r *PrivateKeySetRepo) Set(ks key.KeySet) error {
|
||||||
|
@ -131,7 +133,14 @@ func (r *PrivateKeySetRepo) Set(ks key.KeySet) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := pcrypto.AESEncrypt(j, r.active())
|
var v []byte
|
||||||
|
|
||||||
|
if r.useOldFormat {
|
||||||
|
v, err = pcrypto.AESEncrypt(j, r.active())
|
||||||
|
} else {
|
||||||
|
v, err = pcrypto.Encrypt(j, r.active())
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -159,7 +168,13 @@ func (r *PrivateKeySetRepo) Get() (key.KeySet, error) {
|
||||||
var pks *key.PrivateKeySet
|
var pks *key.PrivateKeySet
|
||||||
for _, secret := range r.secrets {
|
for _, secret := range r.secrets {
|
||||||
var j []byte
|
var j []byte
|
||||||
j, err = pcrypto.AESDecrypt(b.Value, secret)
|
|
||||||
|
if r.useOldFormat {
|
||||||
|
j, err = pcrypto.AESDecrypt(b.Value, secret)
|
||||||
|
} else {
|
||||||
|
j, err = pcrypto.Decrypt(b.Value, secret)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewPrivateKeySetRepoInvalidKey(t *testing.T) {
|
func TestNewPrivateKeySetRepoInvalidKey(t *testing.T) {
|
||||||
_, err := NewPrivateKeySetRepo(nil, []byte("sharks"))
|
_, err := NewPrivateKeySetRepo(nil, false, []byte("sharks"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected non-nil error")
|
t.Fatalf("Expected non-nil error")
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,12 +155,12 @@ func TestDBPrivateKeySetRepoSetGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
setRepo, err := db.NewPrivateKeySetRepo(connect(t), tt.setSecrets...)
|
setRepo, err := db.NewPrivateKeySetRepo(connect(t), false, tt.setSecrets...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
getRepo, err := db.NewPrivateKeySetRepo(connect(t), tt.getSecrets...)
|
getRepo, err := db.NewPrivateKeySetRepo(connect(t), false, tt.getSecrets...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,12 @@ func unpad(paddedtext []byte) ([]byte, error) {
|
||||||
return paddedtext[:length-(pad)], nil
|
return paddedtext[:length-(pad)], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// **DEPRECATED** AESEncrypt encrypts a payloaded with an AES cipher.
|
// AESEncrypt encrypts a payload using AES-CBC and PKCS#7 padding.
|
||||||
// The returned ciphertext has three notable properties:
|
// The returned ciphertext has three notable properties:
|
||||||
// 1. ciphertext is aligned to the standard AES block size
|
// 1. ciphertext is aligned to the standard AES block size
|
||||||
// 2. ciphertext is padded using PKCS#7
|
// 2. ciphertext is padded using PKCS#7
|
||||||
// 3. IV is prepended to the ciphertext
|
// 3. IV is prepended to the ciphertext
|
||||||
|
// This function is DEPRECATED. Use Encrypt() instead.
|
||||||
func AESEncrypt(plaintext, key []byte) ([]byte, error) {
|
func AESEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||||
plaintext, err := pad(plaintext, aes.BlockSize)
|
plaintext, err := pad(plaintext, aes.BlockSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -63,11 +64,12 @@ func AESEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||||
return ciphertext, nil
|
return ciphertext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// **DEPRECATED** AESDecrypt decrypts an encrypted payload with an AES cipher.
|
// AESDecrypt decrypts a payload encrypted using AES-CBC and PKCS#7 padding.
|
||||||
// The decryption algorithm makes three assumptions:
|
// The decryption algorithm makes three assumptions:
|
||||||
// 1. ciphertext is aligned to the standard AES block size
|
// 1. ciphertext is aligned to the standard AES block size
|
||||||
// 2. ciphertext is padded using PKCS#7
|
// 2. ciphertext is padded using PKCS#7
|
||||||
// 3. the IV is prepended to ciphertext
|
// 3. the IV is prepended to ciphertext
|
||||||
|
// This function is DEPRECATED. Use Decrypt() instead.
|
||||||
func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
|
func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||||
if len(ciphertext) < aes.BlockSize {
|
if len(ciphertext) < aes.BlockSize {
|
||||||
return nil, errors.New("ciphertext too short")
|
return nil, errors.New("ciphertext too short")
|
||||||
|
@ -97,8 +99,9 @@ func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||||
return unpad(plaintext)
|
return unpad(plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes plaintext and a key, returns ciphertext or error
|
// Encrypt encrypts data using 256-bit AES-GCM.
|
||||||
// Output takes the form nonce|ciphertext|tag where '|' indicates concatenation
|
// This both hides the content of the data and provides a check that it hasn't been altered.
|
||||||
|
// Output takes the form nonce|ciphertext|tag where '|' indicates concatenation.
|
||||||
func Encrypt(plaintext, key []byte) (ciphertext []byte, err error) {
|
func Encrypt(plaintext, key []byte) (ciphertext []byte, err error) {
|
||||||
if len(key) != aesKeySize {
|
if len(key) != aesKeySize {
|
||||||
return nil, aes.KeySizeError(len(key))
|
return nil, aes.KeySizeError(len(key))
|
||||||
|
@ -122,8 +125,9 @@ func Encrypt(plaintext, key []byte) (ciphertext []byte, err error) {
|
||||||
return gcm.Seal(nonce, nonce, plaintext, nil), nil
|
return gcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes ciphertext and a key, returns plaintext or error
|
// Decrypt decrypts data using 256-bit AES-GCM.
|
||||||
// Expects input form nonce|ciphertext|tag where '|' indicates concatenation
|
// This both hides the content of the data and provides a check that it hasn't been altered.
|
||||||
|
// Expects input form nonce|ciphertext|tag where '|' indicates concatenation.
|
||||||
func Decrypt(ciphertext, key []byte) (plaintext []byte, err error) {
|
func Decrypt(ciphertext, key []byte) (plaintext []byte, err error) {
|
||||||
if len(key) != aesKeySize {
|
if len(key) != aesKeySize {
|
||||||
return nil, aes.KeySizeError(len(key))
|
return nil, aes.KeySizeError(len(key))
|
|
@ -49,6 +49,7 @@ type SingleServerConfig struct {
|
||||||
type MultiServerConfig struct {
|
type MultiServerConfig struct {
|
||||||
KeySecrets [][]byte
|
KeySecrets [][]byte
|
||||||
DatabaseConfig db.Config
|
DatabaseConfig db.Config
|
||||||
|
UseOldFormat bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *ServerConfig) Server() (*Server, error) {
|
func (cfg *ServerConfig) Server() (*Server, error) {
|
||||||
|
@ -159,7 +160,7 @@ func (cfg *MultiServerConfig) Configure(srv *Server) error {
|
||||||
return fmt.Errorf("unable to initialize database connection: %v", err)
|
return fmt.Errorf("unable to initialize database connection: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
kRepo, err := db.NewPrivateKeySetRepo(dbc, cfg.KeySecrets...)
|
kRepo, err := db.NewPrivateKeySetRepo(dbc, cfg.UseOldFormat, cfg.KeySecrets...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err)
|
return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue