Commit 07a4d444 authored by George Tankersley's avatar George Tankersley

pkg/crypto: replace old crypto with new crypto

parent 8d6474b5
...@@ -32,6 +32,8 @@ func main() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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,
} }
} }
......
...@@ -89,7 +89,7 @@ type privateKeySetBlob struct { ...@@ -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 ...@@ -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 { ...@@ -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) { ...@@ -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 ( ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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 { ...@@ -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)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment