2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2022-08-29 18:18:35 +00:00
|
|
|
package structs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/nomad/helper"
|
2022-08-30 14:59:25 +00:00
|
|
|
"github.com/hashicorp/nomad/helper/crypto"
|
2022-08-29 18:18:35 +00:00
|
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RootKey is used to encrypt and decrypt variables. It is never stored in raft.
|
|
|
|
type RootKey struct {
|
|
|
|
Meta *RootKeyMeta
|
|
|
|
Key []byte // serialized to keystore as base64 blob
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRootKey returns a new root key and its metadata.
|
|
|
|
func NewRootKey(algorithm EncryptionAlgorithm) (*RootKey, error) {
|
|
|
|
meta := NewRootKeyMeta()
|
|
|
|
meta.Algorithm = algorithm
|
|
|
|
|
|
|
|
rootKey := &RootKey{
|
|
|
|
Meta: meta,
|
|
|
|
}
|
|
|
|
|
|
|
|
switch algorithm {
|
|
|
|
case EncryptionAlgorithmAES256GCM:
|
2022-08-30 14:59:25 +00:00
|
|
|
key, err := crypto.Bytes(32)
|
2022-08-29 18:18:35 +00:00
|
|
|
if err != nil {
|
2022-08-30 14:59:25 +00:00
|
|
|
return nil, fmt.Errorf("failed to generate key: %v", err)
|
2022-08-29 18:18:35 +00:00
|
|
|
}
|
|
|
|
rootKey.Key = key
|
|
|
|
}
|
|
|
|
|
|
|
|
return rootKey, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RootKeyMeta is the metadata used to refer to a RootKey. It is
|
|
|
|
// stored in raft.
|
|
|
|
type RootKeyMeta struct {
|
|
|
|
KeyID string // UUID
|
|
|
|
Algorithm EncryptionAlgorithm
|
|
|
|
CreateTime int64
|
|
|
|
CreateIndex uint64
|
|
|
|
ModifyIndex uint64
|
|
|
|
State RootKeyState
|
|
|
|
}
|
|
|
|
|
|
|
|
// RootKeyState enum describes the lifecycle of a root key.
|
|
|
|
type RootKeyState string
|
|
|
|
|
|
|
|
const (
|
2022-11-01 19:00:50 +00:00
|
|
|
RootKeyStateInactive RootKeyState = "inactive"
|
|
|
|
RootKeyStateActive = "active"
|
|
|
|
RootKeyStateRekeying = "rekeying"
|
|
|
|
|
|
|
|
// RootKeyStateDeprecated is, itself, deprecated and is no longer in
|
|
|
|
// use. For backwards compatibility, any existing keys with this state will
|
|
|
|
// be treated as RootKeyStateInactive
|
|
|
|
RootKeyStateDeprecated = "deprecated"
|
2022-08-29 18:18:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// NewRootKeyMeta returns a new RootKeyMeta with default values
|
|
|
|
func NewRootKeyMeta() *RootKeyMeta {
|
|
|
|
now := time.Now().UTC().UnixNano()
|
|
|
|
return &RootKeyMeta{
|
|
|
|
KeyID: uuid.Generate(),
|
|
|
|
Algorithm: EncryptionAlgorithmAES256GCM,
|
|
|
|
State: RootKeyStateInactive,
|
|
|
|
CreateTime: now,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RootKeyMetaStub is for serializing root key metadata to the
|
|
|
|
// keystore, not for the List API. It excludes frequently-changing
|
|
|
|
// fields such as ModifyIndex so we don't have to sync them to the
|
|
|
|
// on-disk keystore when the fields are already in raft.
|
|
|
|
type RootKeyMetaStub struct {
|
|
|
|
KeyID string
|
|
|
|
Algorithm EncryptionAlgorithm
|
|
|
|
CreateTime int64
|
|
|
|
State RootKeyState
|
|
|
|
}
|
|
|
|
|
|
|
|
// Active indicates his key is the one currently being used for
|
|
|
|
// crypto operations (at most one key can be Active)
|
|
|
|
func (rkm *RootKeyMeta) Active() bool {
|
|
|
|
return rkm.State == RootKeyStateActive
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rkm *RootKeyMeta) SetActive() {
|
|
|
|
rkm.State = RootKeyStateActive
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rekeying indicates that variables encrypted with this key should be
|
|
|
|
// rekeyed
|
|
|
|
func (rkm *RootKeyMeta) Rekeying() bool {
|
|
|
|
return rkm.State == RootKeyStateRekeying
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rkm *RootKeyMeta) SetRekeying() {
|
|
|
|
rkm.State = RootKeyStateRekeying
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rkm *RootKeyMeta) SetInactive() {
|
|
|
|
rkm.State = RootKeyStateInactive
|
|
|
|
}
|
|
|
|
|
2022-11-01 19:00:50 +00:00
|
|
|
// Inactive indicates that this key is no longer being used to encrypt new
|
|
|
|
// variables or workload identities.
|
|
|
|
func (rkm *RootKeyMeta) Inactive() bool {
|
|
|
|
return rkm.State == RootKeyStateInactive || rkm.State == RootKeyStateDeprecated
|
2022-08-29 18:18:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (rkm *RootKeyMeta) Stub() *RootKeyMetaStub {
|
|
|
|
if rkm == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &RootKeyMetaStub{
|
|
|
|
KeyID: rkm.KeyID,
|
|
|
|
Algorithm: rkm.Algorithm,
|
|
|
|
CreateTime: rkm.CreateTime,
|
|
|
|
State: rkm.State,
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
func (rkm *RootKeyMeta) Copy() *RootKeyMeta {
|
|
|
|
if rkm == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
out := *rkm
|
|
|
|
return &out
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rkm *RootKeyMeta) Validate() error {
|
|
|
|
if rkm == nil {
|
|
|
|
return fmt.Errorf("root key metadata is required")
|
|
|
|
}
|
|
|
|
if rkm.KeyID == "" || !helper.IsUUID(rkm.KeyID) {
|
|
|
|
return fmt.Errorf("root key UUID is required")
|
|
|
|
}
|
|
|
|
if rkm.Algorithm == "" {
|
|
|
|
return fmt.Errorf("root key algorithm is required")
|
|
|
|
}
|
|
|
|
switch rkm.State {
|
|
|
|
case RootKeyStateInactive, RootKeyStateActive,
|
|
|
|
RootKeyStateRekeying, RootKeyStateDeprecated:
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("root key state %q is invalid", rkm.State)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-08-30 14:59:25 +00:00
|
|
|
// KeyEncryptionKeyWrapper is the struct that gets serialized for the on-disk
|
|
|
|
// KMS wrapper. This struct includes the server-specific key-wrapping key and
|
|
|
|
// should never be sent over RPC.
|
|
|
|
type KeyEncryptionKeyWrapper struct {
|
|
|
|
Meta *RootKeyMeta
|
|
|
|
EncryptedDataEncryptionKey []byte `json:"DEK"`
|
|
|
|
KeyEncryptionKey []byte `json:"KEK"`
|
|
|
|
}
|
|
|
|
|
2022-08-29 18:18:35 +00:00
|
|
|
// EncryptionAlgorithm chooses which algorithm is used for
|
|
|
|
// encrypting / decrypting entries with this key
|
|
|
|
type EncryptionAlgorithm string
|
|
|
|
|
|
|
|
const (
|
|
|
|
EncryptionAlgorithmAES256GCM EncryptionAlgorithm = "aes256-gcm"
|
|
|
|
)
|
|
|
|
|
2022-08-30 14:59:25 +00:00
|
|
|
// KeyringRotateRootKeyRequest is the argument to the Keyring.Rotate RPC
|
2022-08-29 18:18:35 +00:00
|
|
|
type KeyringRotateRootKeyRequest struct {
|
|
|
|
Algorithm EncryptionAlgorithm
|
|
|
|
Full bool
|
|
|
|
WriteRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyringRotateRootKeyResponse returns the full key metadata
|
|
|
|
type KeyringRotateRootKeyResponse struct {
|
|
|
|
Key *RootKeyMeta
|
|
|
|
WriteMeta
|
|
|
|
}
|
|
|
|
|
2022-08-30 14:59:25 +00:00
|
|
|
// KeyringListRootKeyMetaRequest is the argument to the Keyring.List RPC
|
2022-08-29 18:18:35 +00:00
|
|
|
type KeyringListRootKeyMetaRequest struct {
|
|
|
|
QueryOptions
|
|
|
|
}
|
|
|
|
|
2022-08-30 14:59:25 +00:00
|
|
|
// KeyringListRootKeyMetaRequest is the response value of the List RPC
|
2022-08-29 18:18:35 +00:00
|
|
|
type KeyringListRootKeyMetaResponse struct {
|
|
|
|
Keys []*RootKeyMeta
|
|
|
|
QueryMeta
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyringUpdateRootKeyRequest is used internally for key replication
|
|
|
|
// only and for keyring restores. The RootKeyMeta will be extracted
|
|
|
|
// for applying to the FSM with the KeyringUpdateRootKeyMetaRequest
|
|
|
|
// (see below)
|
|
|
|
type KeyringUpdateRootKeyRequest struct {
|
|
|
|
RootKey *RootKey
|
|
|
|
Rekey bool
|
|
|
|
WriteRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
type KeyringUpdateRootKeyResponse struct {
|
|
|
|
WriteMeta
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyringGetRootKeyRequest is used internally for key replication
|
|
|
|
// only and for keyring restores.
|
|
|
|
type KeyringGetRootKeyRequest struct {
|
|
|
|
KeyID string
|
|
|
|
QueryOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
type KeyringGetRootKeyResponse struct {
|
|
|
|
Key *RootKey
|
|
|
|
QueryMeta
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyringUpdateRootKeyMetaRequest is used internally for key
|
|
|
|
// replication so that we have a request wrapper for writing the
|
|
|
|
// metadata to the FSM without including the key material
|
|
|
|
type KeyringUpdateRootKeyMetaRequest struct {
|
|
|
|
RootKeyMeta *RootKeyMeta
|
|
|
|
Rekey bool
|
|
|
|
WriteRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
type KeyringUpdateRootKeyMetaResponse struct {
|
|
|
|
WriteMeta
|
|
|
|
}
|
|
|
|
|
|
|
|
type KeyringDeleteRootKeyRequest struct {
|
|
|
|
KeyID string
|
|
|
|
WriteRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
type KeyringDeleteRootKeyResponse struct {
|
|
|
|
WriteMeta
|
|
|
|
}
|