1package vault 2 3import ( 4 "bytes" 5 "context" 6 "encoding/base64" 7 "encoding/json" 8 "fmt" 9 "sync/atomic" 10 11 "github.com/golang/protobuf/proto" 12 "github.com/hashicorp/errwrap" 13 "github.com/hashicorp/vault/sdk/helper/jsonutil" 14 "github.com/hashicorp/vault/sdk/physical" 15 "github.com/hashicorp/vault/vault/seal" 16 "github.com/keybase/go-crypto/openpgp" 17 "github.com/keybase/go-crypto/openpgp/packet" 18) 19 20const ( 21 // barrierSealConfigPath is the path used to store our seal configuration. 22 // This value is stored in plaintext, since we must be able to read it even 23 // with the Vault sealed. This is required so that we know how many secret 24 // parts must be used to reconstruct the master key. 25 barrierSealConfigPath = "core/seal-config" 26 27 // recoverySealConfigPath is the path to the recovery key seal 28 // configuration. It lives inside the barrier. 29 // DEPRECATED: Use recoverySealConfigPlaintextPath instead. 30 recoverySealConfigPath = "core/recovery-seal-config" 31 32 // recoverySealConfigPlaintextPath is the path to the recovery key seal 33 // configuration. This is stored in plaintext so that we can perform 34 // auto-unseal. 35 recoverySealConfigPlaintextPath = "core/recovery-config" 36 37 // recoveryKeyPath is the path to the recovery key 38 recoveryKeyPath = "core/recovery-key" 39 40 // StoredBarrierKeysPath is the path used for storing HSM-encrypted unseal keys 41 StoredBarrierKeysPath = "core/hsm/barrier-unseal-keys" 42 43 // hsmStoredIVPath is the path to the initialization vector for stored keys 44 hsmStoredIVPath = "core/hsm/iv" 45) 46 47const ( 48 RecoveryTypeUnsupported = "unsupported" 49 RecoveryTypeShamir = "shamir" 50) 51 52type StoredKeysSupport int 53 54const ( 55 // The 0 value of StoredKeysSupport is an invalid option 56 StoredKeysInvalid StoredKeysSupport = iota 57 StoredKeysNotSupported 58 StoredKeysSupportedGeneric 59 StoredKeysSupportedShamirMaster 60) 61 62func (s StoredKeysSupport) String() string { 63 switch s { 64 case StoredKeysNotSupported: 65 return "Old-style Shamir" 66 case StoredKeysSupportedGeneric: 67 return "AutoUnseal" 68 case StoredKeysSupportedShamirMaster: 69 return "New-style Shamir" 70 default: 71 return "Invalid StoredKeys type" 72 } 73} 74 75type Seal interface { 76 SetCore(*Core) 77 Init(context.Context) error 78 Finalize(context.Context) error 79 80 StoredKeysSupported() StoredKeysSupport 81 SealWrapable() bool 82 SetStoredKeys(context.Context, [][]byte) error 83 GetStoredKeys(context.Context) ([][]byte, error) 84 85 BarrierType() string 86 BarrierConfig(context.Context) (*SealConfig, error) 87 SetBarrierConfig(context.Context, *SealConfig) error 88 SetCachedBarrierConfig(*SealConfig) 89 90 RecoveryKeySupported() bool 91 RecoveryType() string 92 RecoveryConfig(context.Context) (*SealConfig, error) 93 RecoveryKey(context.Context) ([]byte, error) 94 SetRecoveryConfig(context.Context, *SealConfig) error 95 SetCachedRecoveryConfig(*SealConfig) 96 SetRecoveryKey(context.Context, []byte) error 97 VerifyRecoveryKey(context.Context, []byte) error 98 99 GetAccess() seal.Access 100} 101 102type defaultSeal struct { 103 access seal.Access 104 config atomic.Value 105 core *Core 106} 107 108func NewDefaultSeal(lowLevel seal.Access) Seal { 109 ret := &defaultSeal{ 110 access: lowLevel, 111 } 112 ret.config.Store((*SealConfig)(nil)) 113 return ret 114} 115 116func (d *defaultSeal) SealWrapable() bool { 117 return false 118} 119 120func (d *defaultSeal) checkCore() error { 121 if d.core == nil { 122 return fmt.Errorf("seal does not have a core set") 123 } 124 return nil 125} 126 127func (d *defaultSeal) GetAccess() seal.Access { 128 return d.access 129} 130 131func (d *defaultSeal) SetAccess(access seal.Access) { 132 d.access = access 133} 134 135func (d *defaultSeal) SetCore(core *Core) { 136 d.core = core 137} 138 139func (d *defaultSeal) Init(ctx context.Context) error { 140 return nil 141} 142 143func (d *defaultSeal) Finalize(ctx context.Context) error { 144 return nil 145} 146 147func (d *defaultSeal) BarrierType() string { 148 return seal.Shamir 149} 150 151func (d *defaultSeal) StoredKeysSupported() StoredKeysSupport { 152 isLegacy, err := d.LegacySeal() 153 if err != nil { 154 if d.core != nil && d.core.logger != nil { 155 d.core.logger.Error("no seal config found, can't determine if legacy or new-style shamir") 156 } 157 return StoredKeysInvalid 158 } 159 switch { 160 case isLegacy: 161 return StoredKeysNotSupported 162 default: 163 return StoredKeysSupportedShamirMaster 164 } 165} 166 167func (d *defaultSeal) RecoveryKeySupported() bool { 168 return false 169} 170 171func (d *defaultSeal) SetStoredKeys(ctx context.Context, keys [][]byte) error { 172 isLegacy, err := d.LegacySeal() 173 if err != nil { 174 return err 175 } 176 if isLegacy { 177 return fmt.Errorf("stored keys are not supported") 178 } 179 return writeStoredKeys(ctx, d.core.physical, d.access, keys) 180} 181 182func (d *defaultSeal) LegacySeal() (bool, error) { 183 cfg := d.config.Load().(*SealConfig) 184 if cfg == nil { 185 return false, fmt.Errorf("no seal config found, can't determine if legacy or new-style shamir") 186 } 187 return cfg.StoredShares == 0, nil 188} 189 190func (d *defaultSeal) GetStoredKeys(ctx context.Context) ([][]byte, error) { 191 isLegacy, err := d.LegacySeal() 192 if err != nil { 193 return nil, err 194 } 195 if isLegacy { 196 return nil, fmt.Errorf("stored keys are not supported") 197 } 198 keys, err := readStoredKeys(ctx, d.core.physical, d.access) 199 return keys, err 200} 201 202func (d *defaultSeal) BarrierConfig(ctx context.Context) (*SealConfig, error) { 203 cfg := d.config.Load().(*SealConfig) 204 if cfg != nil { 205 return cfg.Clone(), nil 206 } 207 208 if err := d.checkCore(); err != nil { 209 return nil, err 210 } 211 212 // Fetch the core configuration 213 pe, err := d.core.physical.Get(ctx, barrierSealConfigPath) 214 if err != nil { 215 d.core.logger.Error("failed to read seal configuration", "error", err) 216 return nil, errwrap.Wrapf("failed to check seal configuration: {{err}}", err) 217 } 218 219 // If the seal configuration is missing, we are not initialized 220 if pe == nil { 221 d.core.logger.Info("seal configuration missing, not initialized") 222 return nil, nil 223 } 224 225 var conf SealConfig 226 227 // Decode the barrier entry 228 if err := jsonutil.DecodeJSON(pe.Value, &conf); err != nil { 229 d.core.logger.Error("failed to decode seal configuration", "error", err) 230 return nil, errwrap.Wrapf("failed to decode seal configuration: {{err}}", err) 231 } 232 233 switch conf.Type { 234 // This case should not be valid for other types as only this is the default 235 case "": 236 conf.Type = d.BarrierType() 237 case d.BarrierType(): 238 default: 239 d.core.logger.Error("barrier seal type does not match expected type", "barrier_seal_type", conf.Type, "loaded_seal_type", d.BarrierType()) 240 return nil, fmt.Errorf("barrier seal type of %q does not match expected type of %q", conf.Type, d.BarrierType()) 241 } 242 243 // Check for a valid seal configuration 244 if err := conf.Validate(); err != nil { 245 d.core.logger.Error("invalid seal configuration", "error", err) 246 return nil, errwrap.Wrapf("seal validation failed: {{err}}", err) 247 } 248 249 d.config.Store(&conf) 250 return conf.Clone(), nil 251} 252 253func (d *defaultSeal) SetBarrierConfig(ctx context.Context, config *SealConfig) error { 254 if err := d.checkCore(); err != nil { 255 return err 256 } 257 258 // Provide a way to wipe out the cached value (also prevents actually 259 // saving a nil config) 260 if config == nil { 261 d.config.Store((*SealConfig)(nil)) 262 return nil 263 } 264 265 config.Type = d.BarrierType() 266 267 // If we are doing a raft unseal we do not want to persist the barrier config 268 // because storage isn't setup yet. 269 if d.core.isRaftUnseal() { 270 d.config.Store(config.Clone()) 271 return nil 272 } 273 274 // Encode the seal configuration 275 buf, err := json.Marshal(config) 276 if err != nil { 277 return errwrap.Wrapf("failed to encode seal configuration: {{err}}", err) 278 } 279 280 // Store the seal configuration 281 pe := &physical.Entry{ 282 Key: barrierSealConfigPath, 283 Value: buf, 284 } 285 286 if err := d.core.physical.Put(ctx, pe); err != nil { 287 d.core.logger.Error("failed to write seal configuration", "error", err) 288 return errwrap.Wrapf("failed to write seal configuration: {{err}}", err) 289 } 290 291 d.config.Store(config.Clone()) 292 293 return nil 294} 295 296func (d *defaultSeal) SetCachedBarrierConfig(config *SealConfig) { 297 d.config.Store(config) 298} 299 300func (d *defaultSeal) RecoveryType() string { 301 return RecoveryTypeUnsupported 302} 303 304func (d *defaultSeal) RecoveryConfig(ctx context.Context) (*SealConfig, error) { 305 return nil, fmt.Errorf("recovery not supported") 306} 307 308func (d *defaultSeal) RecoveryKey(ctx context.Context) ([]byte, error) { 309 return nil, fmt.Errorf("recovery not supported") 310} 311 312func (d *defaultSeal) SetRecoveryConfig(ctx context.Context, config *SealConfig) error { 313 return fmt.Errorf("recovery not supported") 314} 315 316func (d *defaultSeal) SetCachedRecoveryConfig(config *SealConfig) { 317} 318 319func (d *defaultSeal) VerifyRecoveryKey(ctx context.Context, key []byte) error { 320 return fmt.Errorf("recovery not supported") 321} 322 323func (d *defaultSeal) SetRecoveryKey(ctx context.Context, key []byte) error { 324 return fmt.Errorf("recovery not supported") 325} 326 327// SealConfig is used to describe the seal configuration 328type SealConfig struct { 329 // The type, for sanity checking 330 Type string `json:"type" mapstructure:"type"` 331 332 // SecretShares is the number of shares the secret is split into. This is 333 // the N value of Shamir. 334 SecretShares int `json:"secret_shares" mapstructure:"secret_shares"` 335 336 // SecretThreshold is the number of parts required to open the vault. This 337 // is the T value of Shamir. 338 SecretThreshold int `json:"secret_threshold" mapstructure:"secret_threshold"` 339 340 // PGPKeys is the array of public PGP keys used, if requested, to encrypt 341 // the output unseal tokens. If provided, it sets the value of 342 // SecretShares. Ordering is important. 343 PGPKeys []string `json:"pgp_keys" mapstructure:"pgp_keys"` 344 345 // Nonce is a nonce generated by Vault used to ensure that when unseal keys 346 // are submitted for a rekey operation, the rekey operation itself is the 347 // one intended. This prevents hijacking of the rekey operation, since it 348 // is unauthenticated. 349 Nonce string `json:"nonce" mapstructure:"nonce"` 350 351 // Backup indicates whether or not a backup of PGP-encrypted unseal keys 352 // should be stored at coreUnsealKeysBackupPath after successful rekeying. 353 Backup bool `json:"backup" mapstructure:"backup"` 354 355 // How many keys to store, for seals that support storage. Always 0 or 1. 356 StoredShares int `json:"stored_shares" mapstructure:"stored_shares"` 357 358 // Stores the progress of the rekey operation (key shares) 359 RekeyProgress [][]byte `json:"-"` 360 361 // VerificationRequired indicates that after a rekey validation must be 362 // performed (via providing shares from the new key) before the new key is 363 // actually installed. This is omitted from JSON as we don't persist the 364 // new key, it lives only in memory. 365 VerificationRequired bool `json:"-"` 366 367 // VerificationKey is the new key that we will roll to after successful 368 // validation 369 VerificationKey []byte `json:"-"` 370 371 // VerificationNonce stores the current operation nonce for verification 372 VerificationNonce string `json:"-"` 373 374 // Stores the progress of the verification operation (key shares) 375 VerificationProgress [][]byte `json:"-"` 376} 377 378// Validate is used to sanity check the seal configuration 379func (s *SealConfig) Validate() error { 380 if s.SecretShares < 1 { 381 return fmt.Errorf("shares must be at least one") 382 } 383 if s.SecretThreshold < 1 { 384 return fmt.Errorf("threshold must be at least one") 385 } 386 if s.SecretShares > 1 && s.SecretThreshold == 1 { 387 return fmt.Errorf("threshold must be greater than one for multiple shares") 388 } 389 if s.SecretShares > 255 { 390 return fmt.Errorf("shares must be less than 256") 391 } 392 if s.SecretThreshold > 255 { 393 return fmt.Errorf("threshold must be less than 256") 394 } 395 if s.SecretThreshold > s.SecretShares { 396 return fmt.Errorf("threshold cannot be larger than shares") 397 } 398 if s.StoredShares > 1 { 399 return fmt.Errorf("stored keys cannot be larger than 1") 400 } 401 if len(s.PGPKeys) > 0 && len(s.PGPKeys) != s.SecretShares { 402 return fmt.Errorf("count mismatch between number of provided PGP keys and number of shares") 403 } 404 if len(s.PGPKeys) > 0 { 405 for _, keystring := range s.PGPKeys { 406 data, err := base64.StdEncoding.DecodeString(keystring) 407 if err != nil { 408 return errwrap.Wrapf("error decoding given PGP key: {{err}}", err) 409 } 410 _, err = openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(data))) 411 if err != nil { 412 return errwrap.Wrapf("error parsing given PGP key: {{err}}", err) 413 } 414 } 415 } 416 return nil 417} 418 419func (s *SealConfig) Clone() *SealConfig { 420 ret := &SealConfig{ 421 Type: s.Type, 422 SecretShares: s.SecretShares, 423 SecretThreshold: s.SecretThreshold, 424 Nonce: s.Nonce, 425 Backup: s.Backup, 426 StoredShares: s.StoredShares, 427 VerificationRequired: s.VerificationRequired, 428 VerificationNonce: s.VerificationNonce, 429 } 430 if len(s.PGPKeys) > 0 { 431 ret.PGPKeys = make([]string, len(s.PGPKeys)) 432 copy(ret.PGPKeys, s.PGPKeys) 433 } 434 if len(s.VerificationKey) > 0 { 435 ret.VerificationKey = make([]byte, len(s.VerificationKey)) 436 copy(ret.VerificationKey, s.VerificationKey) 437 } 438 return ret 439} 440 441func writeStoredKeys(ctx context.Context, storage physical.Backend, encryptor seal.Encryptor, keys [][]byte) error { 442 if keys == nil { 443 return fmt.Errorf("keys were nil") 444 } 445 if len(keys) == 0 { 446 return fmt.Errorf("no keys provided") 447 } 448 449 buf, err := json.Marshal(keys) 450 if err != nil { 451 return errwrap.Wrapf("failed to encode keys for storage: {{err}}", err) 452 } 453 454 // Encrypt and marshal the keys 455 blobInfo, err := encryptor.Encrypt(ctx, buf) 456 if err != nil { 457 return errwrap.Wrapf("failed to encrypt keys for storage: {{err}}", err) 458 } 459 460 value, err := proto.Marshal(blobInfo) 461 if err != nil { 462 return errwrap.Wrapf("failed to marshal value for storage: {{err}}", err) 463 } 464 465 // Store the seal configuration. 466 pe := &physical.Entry{ 467 Key: StoredBarrierKeysPath, 468 Value: value, 469 } 470 471 if err := storage.Put(ctx, pe); err != nil { 472 return errwrap.Wrapf("failed to write keys to storage: {{err}}", err) 473 } 474 475 return nil 476} 477 478func readStoredKeys(ctx context.Context, storage physical.Backend, encryptor seal.Encryptor) ([][]byte, error) { 479 pe, err := storage.Get(ctx, StoredBarrierKeysPath) 480 if err != nil { 481 return nil, errwrap.Wrapf("failed to fetch stored keys: {{err}}", err) 482 } 483 484 // This is not strictly an error; we may not have any stored keys, for 485 // instance, if we're not initialized 486 if pe == nil { 487 return nil, nil 488 } 489 490 blobInfo := &physical.EncryptedBlobInfo{} 491 if err := proto.Unmarshal(pe.Value, blobInfo); err != nil { 492 return nil, errwrap.Wrapf("failed to proto decode stored keys: {{err}}", err) 493 } 494 495 pt, err := encryptor.Decrypt(ctx, blobInfo) 496 if err != nil { 497 return nil, errwrap.Wrapf("failed to decrypt encrypted stored keys: {{err}}", err) 498 } 499 500 // Decode the barrier entry 501 var keys [][]byte 502 if err := json.Unmarshal(pt, &keys); err != nil { 503 return nil, fmt.Errorf("failed to decode stored keys: %v", err) 504 } 505 506 return keys, nil 507} 508