forked from mystiq/dex
c8feb5c33d
The first secret is used to encrypt, the rest are for decryption; if the first doesn't work, the rest are tried in order. The makes it possible to rotate keys.
187 lines
3.4 KiB
Go
187 lines
3.4 KiB
Go
package db
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/coopernurse/gorp"
|
|
"github.com/lib/pq"
|
|
|
|
pcrypto "github.com/coreos/dex/pkg/crypto"
|
|
"github.com/coreos/go-oidc/key"
|
|
)
|
|
|
|
const (
|
|
keyTableName = "key"
|
|
)
|
|
|
|
var (
|
|
ErrorCannotDecryptKeys = errors.New("Cannot Decrypt Keys")
|
|
)
|
|
|
|
func init() {
|
|
register(table{
|
|
name: keyTableName,
|
|
model: privateKeySetBlob{},
|
|
autoinc: false,
|
|
pkey: []string{"value"},
|
|
})
|
|
}
|
|
|
|
func newPrivateKeySetModel(pks *key.PrivateKeySet) (*privateKeySetModel, error) {
|
|
pkeys := pks.Keys()
|
|
keys := make([]privateKeyModel, len(pkeys))
|
|
for i, pkey := range pkeys {
|
|
keys[i] = privateKeyModel{
|
|
ID: pkey.ID(),
|
|
PKCS1: x509.MarshalPKCS1PrivateKey(pkey.PrivateKey),
|
|
}
|
|
}
|
|
|
|
m := privateKeySetModel{
|
|
Keys: keys,
|
|
ExpiresAt: pks.ExpiresAt(),
|
|
}
|
|
|
|
return &m, nil
|
|
}
|
|
|
|
type privateKeyModel struct {
|
|
ID string `json:"id"`
|
|
PKCS1 []byte `json:"pkcs1"`
|
|
}
|
|
|
|
func (m *privateKeyModel) PrivateKey() (*key.PrivateKey, error) {
|
|
d, err := x509.ParsePKCS1PrivateKey(m.PKCS1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pk := key.PrivateKey{
|
|
KeyID: m.ID,
|
|
PrivateKey: d,
|
|
}
|
|
|
|
return &pk, nil
|
|
}
|
|
|
|
type privateKeySetModel struct {
|
|
Keys []privateKeyModel `json:"keys"`
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
}
|
|
|
|
func (m *privateKeySetModel) PrivateKeySet() (*key.PrivateKeySet, error) {
|
|
keys := make([]*key.PrivateKey, len(m.Keys))
|
|
for i, pkm := range m.Keys {
|
|
pk, err := pkm.PrivateKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keys[i] = pk
|
|
}
|
|
return key.NewPrivateKeySet(keys, m.ExpiresAt), nil
|
|
}
|
|
|
|
type privateKeySetBlob struct {
|
|
Value []byte `db:"value"`
|
|
}
|
|
|
|
func NewPrivateKeySetRepo(dbm *gorp.DbMap, secrets ...[]byte) (*PrivateKeySetRepo, error) {
|
|
for i, secret := range secrets {
|
|
if len(secret) != 32 {
|
|
return nil, fmt.Errorf("key secret %d: expected 32-byte secret", i)
|
|
}
|
|
}
|
|
|
|
r := &PrivateKeySetRepo{
|
|
dbMap: dbm,
|
|
secrets: secrets,
|
|
}
|
|
|
|
return r, nil
|
|
}
|
|
|
|
type PrivateKeySetRepo struct {
|
|
dbMap *gorp.DbMap
|
|
secrets [][]byte
|
|
}
|
|
|
|
func (r *PrivateKeySetRepo) Set(ks key.KeySet) error {
|
|
qt := pq.QuoteIdentifier(keyTableName)
|
|
_, err := r.dbMap.Exec(fmt.Sprintf("DELETE FROM %s", qt))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pks, ok := ks.(*key.PrivateKeySet)
|
|
if !ok {
|
|
return errors.New("unable to cast to PrivateKeySet")
|
|
}
|
|
|
|
m, err := newPrivateKeySetModel(pks)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
j, err := json.Marshal(m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
v, err := pcrypto.AESEncrypt(j, r.active())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b := &privateKeySetBlob{Value: v}
|
|
return r.dbMap.Insert(b)
|
|
}
|
|
|
|
func (r *PrivateKeySetRepo) Get() (key.KeySet, error) {
|
|
qt := pq.QuoteIdentifier(keyTableName)
|
|
objs, err := r.dbMap.Select(&privateKeySetBlob{}, fmt.Sprintf("SELECT * FROM %s", qt))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(objs) == 0 {
|
|
return nil, key.ErrorNoKeys
|
|
}
|
|
|
|
b, ok := objs[0].(*privateKeySetBlob)
|
|
if !ok {
|
|
return nil, errors.New("unable to cast to KeySet")
|
|
}
|
|
|
|
var pks *key.PrivateKeySet
|
|
for _, secret := range r.secrets {
|
|
var j []byte
|
|
j, err = pcrypto.AESDecrypt(b.Value, secret)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
var m privateKeySetModel
|
|
if err = json.Unmarshal(j, &m); err != nil {
|
|
continue
|
|
}
|
|
|
|
pks, err = m.PrivateKeySet()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, ErrorCannotDecryptKeys
|
|
}
|
|
return key.KeySet(pks), nil
|
|
}
|
|
|
|
func (r *PrivateKeySetRepo) active() []byte {
|
|
return r.secrets[0]
|
|
}
|