Files
threshold/internal/kms/repository_root_key.go
T
Jeff Mitchell c3684d20db Fix WithKeyId option (#970)
Currently this would validate that a KeyId was in the returned wrapper
but then return the base wrapper; and if it required a database lookup
it wouldn't validate it anyways.

All uses of this were for decryption so it didn't actually affect
anything to this point (you'd error later instead of sooner) but for any
case where we need to encrypt against a specific version or derive a key
against a specific version this would be broken, in a way that could
lead you to use the wrong key version.

This also properly sends the Key ID around sessions. This worked
currently for the same reason as above but would have broken for any
session in flight once a key was rotated.
2021-03-03 11:23:00 -05:00

147 lines
4.7 KiB
Go

package kms
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
wrapping "github.com/hashicorp/go-kms-wrapping"
)
// CreateRootKey inserts into the repository and returns the new root key and
// root key version. There are no valid options at this time.
func (r *Repository) CreateRootKey(ctx context.Context, keyWrapper wrapping.Wrapper, scopeId string, key []byte, _ ...Option) (*RootKey, *RootKeyVersion, error) {
const op = "kms.(Repository).CreateRootKey"
var returnedRk, returnedKv interface{}
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) error {
var err error
if returnedRk, returnedKv, err = createRootKeyTx(ctx, w, keyWrapper, scopeId, key); err != nil {
return errors.Wrap(err, op)
}
return nil
},
)
if err != nil {
return nil, nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", scopeId)))
}
return returnedRk.(*RootKey), returnedKv.(*RootKeyVersion), nil
}
// createRootKeyTx inserts into the db (via db.Writer) and returns the new root key
// and root key version. This function encapsulates all the work required within
// a db.TxHandler and allows this capability to be shared with the iam repo.
func createRootKeyTx(ctx context.Context, w db.Writer, keyWrapper wrapping.Wrapper, scopeId string, key []byte) (*RootKey, *RootKeyVersion, error) {
const op = "kms.createRootKeyTx"
if scopeId == "" {
return nil, nil, errors.New(errors.InvalidParameter, op, "missing scope id")
}
if keyWrapper == nil {
return nil, nil, errors.New(errors.InvalidParameter, op, "missing key wrapper")
}
if len(key) == 0 {
return nil, nil, errors.New(errors.InvalidParameter, op, "missing key")
}
rk := AllocRootKey()
kv := AllocRootKeyVersion()
id, err := newRootKeyId()
if err != nil {
return nil, nil, errors.Wrap(err, op)
}
rk.PrivateId = id
rk.ScopeId = scopeId
id, err = newRootKeyVersionId()
if err != nil {
return nil, nil, errors.Wrap(err, op)
}
kv.PrivateId = id
kv.RootKeyId = rk.PrivateId
kv.Key = key
if err := kv.Encrypt(ctx, keyWrapper); err != nil {
return nil, nil, errors.Wrap(err, op)
}
// no oplog entries for root keys
if err := w.Create(ctx, &rk); err != nil {
return nil, nil, errors.Wrap(err, op, errors.WithMsg("root keys"))
}
// no oplog entries for root key versions
if err := w.Create(ctx, &kv); err != nil {
return nil, nil, errors.Wrap(err, op, errors.WithMsg("key versions"))
}
return &rk, &kv, nil
}
// LookupRootKey will look up a root key in the repository. If the key is not
// found, it will return nil, nil.
func (r *Repository) LookupRootKey(ctx context.Context, keyWrapper wrapping.Wrapper, privateId string, _ ...Option) (*RootKey, error) {
const op = "kms.(Repository).LookupRootKey"
if privateId == "" {
return nil, errors.New(errors.InvalidParameter, op, "missing private id")
}
if keyWrapper == nil {
return nil, errors.New(errors.InvalidParameter, op, "missing key wrapper")
}
k := AllocRootKey()
k.PrivateId = privateId
if err := r.reader.LookupById(ctx, &k); err != nil {
return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", privateId)))
}
return &k, nil
}
// DeleteRootKey deletes the root key for the provided id from the
// repository returning a count of the number of records deleted. All options
// are ignored.
func (r *Repository) DeleteRootKey(ctx context.Context, privateId string, _ ...Option) (int, error) {
const op = "kms.(Repository).DeleteRootKey"
if privateId == "" {
return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing private id")
}
k := AllocRootKey()
k.PrivateId = privateId
if err := r.reader.LookupById(ctx, &k); err != nil {
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", privateId)))
}
var rowsDeleted int
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(_ db.Reader, w db.Writer) (err error) {
dk := k.Clone()
// no oplog entries for root keys
rowsDeleted, err = w.Delete(ctx, dk)
if err != nil {
return errors.Wrap(err, op)
}
if rowsDeleted > 1 {
return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been deleted")
}
return nil
},
)
if err != nil {
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", privateId)))
}
return rowsDeleted, nil
}
// ListRootKeys will list the root keys. Supports the WithLimit option.
func (r *Repository) ListRootKeys(ctx context.Context, opt ...Option) ([]*RootKey, error) {
const op = "kms.(Repository).ListRootKeys"
var keys []*RootKey
err := r.list(ctx, &keys, "1=1", nil, opt...)
if err != nil {
return nil, errors.Wrap(err, op)
}
return keys, nil
}