package structs import ( "fmt" "time" // note: this is aliased so that it's more noticeable if someone // accidentally swaps it out for math/rand via running goimports cryptorand "crypto/rand" "github.com/hashicorp/nomad/helper" "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: const keyBytes = 32 key := make([]byte, keyBytes) n, err := cryptorand.Read(key) if err != nil { return nil, err } if n < keyBytes { return nil, fmt.Errorf("failed to generate key: entropy exhausted") } 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 ( RootKeyStateInactive RootKeyState = "inactive" RootKeyStateActive = "active" RootKeyStateRekeying = "rekeying" RootKeyStateDeprecated = "deprecated" ) // 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 } // Deprecated indicates that variables encrypted with this key // have been rekeyed func (rkm *RootKeyMeta) Deprecated() bool { return rkm.State == RootKeyStateDeprecated } func (rkm *RootKeyMeta) SetDeprecated() { rkm.State = RootKeyStateDeprecated } 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 } // EncryptionAlgorithm chooses which algorithm is used for // encrypting / decrypting entries with this key type EncryptionAlgorithm string const ( EncryptionAlgorithmAES256GCM EncryptionAlgorithm = "aes256-gcm" ) type KeyringRotateRootKeyRequest struct { Algorithm EncryptionAlgorithm Full bool WriteRequest } // KeyringRotateRootKeyResponse returns the full key metadata type KeyringRotateRootKeyResponse struct { Key *RootKeyMeta WriteMeta } type KeyringListRootKeyMetaRequest struct { // TODO: do we need any fields here? QueryOptions } 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 }