func (c *Core) generateShares(sc *SealConfig) ([]byte, [][]byte, error) { // Generate a master key masterKey, err := c.barrier.GenerateKey() if err != nil { return nil, nil, fmt.Errorf("key generation failed: %v", err) } // Return the master key if only a single key part is used var unsealKeys [][]byte if sc.SecretShares == 1 { unsealKeys = append(unsealKeys, masterKey) } else { // Split the master key using the Shamir algorithm shares, err := shamir.Split(masterKey, sc.SecretShares, sc.SecretThreshold) if err != nil { return nil, nil, fmt.Errorf("failed to generate shares: %v", err) } unsealKeys = shares } // If we have PGP keys, perform the encryption if len(sc.PGPKeys) > 0 { hexEncodedShares := make([][]byte, len(unsealKeys)) for i, _ := range unsealKeys { hexEncodedShares[i] = []byte(hex.EncodeToString(unsealKeys[i])) } _, encryptedShares, err := pgpkeys.EncryptShares(hexEncodedShares, sc.PGPKeys) if err != nil { return nil, nil, err } unsealKeys = encryptedShares } return masterKey, unsealKeys, nil }
// EncryptValue encrypts the given value with the given encryption key. Description // should be set such that errors return a meaningful user-facing response. func EncryptValue(encryptionKey, value, description string) (string, string, error) { fingerprints, encryptedValue, err := pgpkeys.EncryptShares([][]byte{[]byte(value)}, []string{encryptionKey}) if err != nil { return "", "", errwrap.Wrapf(fmt.Sprintf("Error encrypting %s: {{err}}", description), err) } return fingerprints[0], base64.StdEncoding.EncodeToString(encryptedValue[0]), nil }
func encryptPassword(password string, pgpKey string) (string, string, error) { const keybasePrefix = "keybase:" encryptionKey := pgpKey if strings.HasPrefix(pgpKey, keybasePrefix) { publicKeys, err := pgpkeys.FetchKeybasePubkeys([]string{pgpKey}) if err != nil { return "", "", errwrap.Wrapf( fmt.Sprintf("Error retrieving Public Key for %s: {{err}}", pgpKey), err) } encryptionKey = publicKeys[pgpKey] } fingerprints, encrypted, err := pgpkeys.EncryptShares([][]byte{[]byte(password)}, []string{encryptionKey}) if err != nil { return "", "", errwrap.Wrapf( fmt.Sprintf("Error encrypting password for %s: {{err}}", pgpKey), err) } return fingerprints[0], base64.StdEncoding.EncodeToString(encrypted[0]), nil }
// Initialize is used to initialize the Vault with the given // configurations. func (c *Core) Initialize(config *SealConfig) (*InitResult, error) { // Check if the seal configuraiton is valid if err := config.Validate(); err != nil { c.logger.Printf("[ERR] core: invalid seal configuration: %v", err) return nil, fmt.Errorf("invalid seal configuration: %v", err) } // Avoid an initialization race c.stateLock.Lock() defer c.stateLock.Unlock() // Check if we are initialized init, err := c.Initialized() if err != nil { return nil, err } if init { return nil, ErrAlreadyInit } // Encode the seal configuration buf, err := json.Marshal(config) if err != nil { return nil, fmt.Errorf("failed to encode seal configuration: %v", err) } // Store the seal configuration pe := &physical.Entry{ Key: coreSealConfigPath, Value: buf, } if err := c.physical.Put(pe); err != nil { c.logger.Printf("[ERR] core: failed to write seal configuration: %v", err) return nil, fmt.Errorf("failed to write seal configuration: %v", err) } // Generate a master key masterKey, err := c.barrier.GenerateKey() if err != nil { c.logger.Printf("[ERR] core: failed to generate master key: %v", err) return nil, fmt.Errorf("master key generation failed: %v", err) } // Return the master key if only a single key part is used results := new(InitResult) if config.SecretShares == 1 { results.SecretShares = append(results.SecretShares, masterKey) } else { // Split the master key using the Shamir algorithm shares, err := shamir.Split(masterKey, config.SecretShares, config.SecretThreshold) if err != nil { c.logger.Printf("[ERR] core: failed to generate shares: %v", err) return nil, fmt.Errorf("failed to generate shares: %v", err) } results.SecretShares = shares } if len(config.PGPKeys) > 0 { encryptedShares, err := pgpkeys.EncryptShares(results.SecretShares, config.PGPKeys) if err != nil { return nil, err } results.SecretShares = encryptedShares } // Initialize the barrier if err := c.barrier.Initialize(masterKey); err != nil { c.logger.Printf("[ERR] core: failed to initialize barrier: %v", err) return nil, fmt.Errorf("failed to initialize barrier: %v", err) } c.logger.Printf("[INFO] core: security barrier initialized (shares: %d, threshold %d)", config.SecretShares, config.SecretThreshold) // Unseal the barrier if err := c.barrier.Unseal(masterKey); err != nil { c.logger.Printf("[ERR] core: failed to unseal barrier: %v", err) return nil, fmt.Errorf("failed to unseal barrier: %v", err) } // Ensure the barrier is re-sealed defer func() { if err := c.barrier.Seal(); err != nil { c.logger.Printf("[ERR] core: failed to seal barrier: %v", err) } }() // Perform initial setup if err := c.postUnseal(); err != nil { c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err) return nil, err } // Generate a new root token rootToken, err := c.tokenStore.RootToken() if err != nil { c.logger.Printf("[ERR] core: root token generation failed: %v", err) return nil, err } results.RootToken = rootToken.ID c.logger.Printf("[INFO] core: root token generated") // Prepare to re-seal if err := c.preSeal(); err != nil { c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err) return nil, err } return results, nil }
// RekeyUpdate is used to provide a new key part func (c *Core) RekeyUpdate(key []byte) (*RekeyResult, error) { // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} } if len(key) > max { return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} } // Get the seal configuration config, err := c.SealConfig() if err != nil { return nil, err } // Ensure the barrier is initialized if config == nil { return nil, ErrNotInit } // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { return nil, ErrSealed } if c.standby { return nil, ErrStandby } c.rekeyLock.Lock() defer c.rekeyLock.Unlock() // Ensure a rekey is in progress if c.rekeyConfig == nil { return nil, fmt.Errorf("no rekey in progress") } // Check if we already have this piece for _, existing := range c.rekeyProgress { if bytes.Equal(existing, key) { return nil, nil } } // Store this key c.rekeyProgress = append(c.rekeyProgress, key) // Check if we don't have enough keys to unlock if len(c.rekeyProgress) < config.SecretThreshold { c.logger.Printf("[DEBUG] core: cannot rekey, have %d of %d keys", len(c.rekeyProgress), config.SecretThreshold) return nil, nil } // Recover the master key var masterKey []byte if config.SecretThreshold == 1 { masterKey = c.rekeyProgress[0] c.rekeyProgress = nil } else { masterKey, err = shamir.Combine(c.rekeyProgress) c.rekeyProgress = nil if err != nil { return nil, fmt.Errorf("failed to compute master key: %v", err) } } // Verify the master key if err := c.barrier.VerifyMaster(masterKey); err != nil { c.logger.Printf("[ERR] core: rekey aborted, master key verification failed: %v", err) return nil, err } // Generate a new master key newMasterKey, err := c.barrier.GenerateKey() if err != nil { c.logger.Printf("[ERR] core: failed to generate master key: %v", err) return nil, fmt.Errorf("master key generation failed: %v", err) } // Return the master key if only a single key part is used results := new(RekeyResult) if c.rekeyConfig.SecretShares == 1 { results.SecretShares = append(results.SecretShares, newMasterKey) } else { // Split the master key using the Shamir algorithm shares, err := shamir.Split(newMasterKey, c.rekeyConfig.SecretShares, c.rekeyConfig.SecretThreshold) if err != nil { c.logger.Printf("[ERR] core: failed to generate shares: %v", err) return nil, fmt.Errorf("failed to generate shares: %v", err) } results.SecretShares = shares } if len(c.rekeyConfig.PGPKeys) > 0 { encryptedShares, err := pgpkeys.EncryptShares(results.SecretShares, c.rekeyConfig.PGPKeys) if err != nil { return nil, err } results.SecretShares = encryptedShares } // Encode the seal configuration buf, err := json.Marshal(c.rekeyConfig) if err != nil { return nil, fmt.Errorf("failed to encode seal configuration: %v", err) } // Rekey the barrier if err := c.barrier.Rekey(newMasterKey); err != nil { c.logger.Printf("[ERR] core: failed to rekey barrier: %v", err) return nil, fmt.Errorf("failed to rekey barrier: %v", err) } c.logger.Printf("[INFO] core: security barrier rekeyed (shares: %d, threshold: %d)", c.rekeyConfig.SecretShares, c.rekeyConfig.SecretThreshold) // Store the seal configuration pe := &physical.Entry{ Key: coreSealConfigPath, Value: buf, } if err := c.physical.Put(pe); err != nil { c.logger.Printf("[ERR] core: failed to update seal configuration: %v", err) return nil, fmt.Errorf("failed to update seal configuration: %v", err) } // Done! c.rekeyProgress = nil c.rekeyConfig = nil return results, nil }
// GenerateRootUpdate is used to provide a new key part func (c *Core) GenerateRootUpdate(key []byte, nonce string) (*GenerateRootResult, error) { // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} } if len(key) > max { return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} } // Get the seal configuration config, err := c.SealConfig() if err != nil { return nil, err } // Ensure the barrier is initialized if config == nil { return nil, ErrNotInit } // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { return nil, ErrSealed } if c.standby { return nil, ErrStandby } c.generateRootLock.Lock() defer c.generateRootLock.Unlock() // Ensure a generateRoot is in progress if c.generateRootConfig == nil { return nil, fmt.Errorf("no root generation in progress") } if nonce != c.generateRootConfig.Nonce { return nil, fmt.Errorf("incorrect nonce supplied; nonce for this root generation operation is %s", c.generateRootConfig.Nonce) } // Check if we already have this piece for _, existing := range c.generateRootProgress { if bytes.Equal(existing, key) { return nil, nil } } // Store this key c.generateRootProgress = append(c.generateRootProgress, key) progress := len(c.generateRootProgress) // Check if we don't have enough keys to unlock if len(c.generateRootProgress) < config.SecretThreshold { c.logger.Printf("[DEBUG] core: cannot generate root, have %d of %d keys", progress, config.SecretThreshold) return &GenerateRootResult{ Progress: progress, Required: config.SecretThreshold, PGPFingerprint: c.generateRootConfig.PGPFingerprint, }, nil } // Recover the master key var masterKey []byte if config.SecretThreshold == 1 { masterKey = c.generateRootProgress[0] c.generateRootProgress = nil } else { masterKey, err = shamir.Combine(c.generateRootProgress) c.generateRootProgress = nil if err != nil { return nil, fmt.Errorf("failed to compute master key: %v", err) } } // Verify the master key if err := c.barrier.VerifyMaster(masterKey); err != nil { c.logger.Printf("[ERR] core: root generation aborted, master key verification failed: %v", err) return nil, err } te, err := c.tokenStore.rootToken() if err != nil { c.logger.Printf("[ERR] core: root token generation failed: %v", err) return nil, err } if te == nil { c.logger.Printf("[ERR] core: got nil token entry back from root generation") return nil, fmt.Errorf("got nil token entry back from root generation") } uuidBytes, err := uuid.ParseUUID(te.ID) if err != nil { c.tokenStore.Revoke(te.ID) c.logger.Printf("[ERR] core: error getting generated token bytes: %v", err) return nil, err } if uuidBytes == nil { c.tokenStore.Revoke(te.ID) c.logger.Printf("[ERR] core: got nil parsed UUID bytes") return nil, fmt.Errorf("got nil parsed UUID bytes") } var tokenBytes []byte // Get the encoded value first so that if there is an error we don't create // the root token. switch { case len(c.generateRootConfig.OTP) > 0: // This function performs decoding checks so rather than decode the OTP, // just encode the value we're passing in. tokenBytes, err = xor.XORBase64(c.generateRootConfig.OTP, base64.StdEncoding.EncodeToString(uuidBytes)) if err != nil { c.tokenStore.Revoke(te.ID) c.logger.Printf("[ERR] core: xor of root token failed: %v", err) return nil, err } case len(c.generateRootConfig.PGPKey) > 0: _, tokenBytesArr, err := pgpkeys.EncryptShares([][]byte{[]byte(te.ID)}, []string{c.generateRootConfig.PGPKey}) if err != nil { c.tokenStore.Revoke(te.ID) c.logger.Printf("[ERR] core: error encrypting new root token: %v", err) return nil, err } tokenBytes = tokenBytesArr[0] default: c.tokenStore.Revoke(te.ID) return nil, fmt.Errorf("unreachable condition") } results := &GenerateRootResult{ Progress: progress, Required: config.SecretThreshold, EncodedRootToken: base64.StdEncoding.EncodeToString(tokenBytes), PGPFingerprint: c.generateRootConfig.PGPFingerprint, } c.logger.Printf("[INFO] core: root generation finished (nonce: %s)", c.generateRootConfig.Nonce) c.generateRootProgress = nil c.generateRootConfig = nil return results, nil }
// RecoveryRekeyUpdate is used to provide a new key part func (c *Core) RecoveryRekeyUpdate(key []byte, nonce string) (*RekeyResult, error) { // Ensure we are already unsealed c.stateLock.RLock() defer c.stateLock.RUnlock() if c.sealed { return nil, ErrSealed } if c.standby { return nil, ErrStandby } // Verify the key length min, max := c.barrier.KeyLength() max += shamir.ShareOverhead if len(key) < min { return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)} } if len(key) > max { return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)} } c.rekeyLock.Lock() defer c.rekeyLock.Unlock() // Get the seal configuration barrierConfig, err := c.seal.BarrierConfig() if err != nil { return nil, err } // Ensure the barrier is initialized if barrierConfig == nil { return nil, ErrNotInit } existingConfig, err := c.seal.RecoveryConfig() if err != nil { return nil, err } // Ensure a rekey is in progress if c.recoveryRekeyConfig == nil { return nil, fmt.Errorf("no rekey in progress") } if nonce != c.recoveryRekeyConfig.Nonce { return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %s", c.recoveryRekeyConfig.Nonce) } // Check if we already have this piece for _, existing := range c.recoveryRekeyProgress { if bytes.Equal(existing, key) { return nil, nil } } // Store this key c.recoveryRekeyProgress = append(c.recoveryRekeyProgress, key) // Check if we don't have enough keys to unlock if len(c.recoveryRekeyProgress) < existingConfig.SecretThreshold { c.logger.Printf("[DEBUG] core: cannot rekey, have %d of %d keys", len(c.recoveryRekeyProgress), existingConfig.SecretThreshold) return nil, nil } // Recover the master key var masterKey []byte if existingConfig.SecretThreshold == 1 { masterKey = c.recoveryRekeyProgress[0] c.recoveryRekeyProgress = nil } else { masterKey, err = shamir.Combine(c.recoveryRekeyProgress) c.recoveryRekeyProgress = nil if err != nil { return nil, fmt.Errorf("failed to compute recovery key: %v", err) } } // Verify the recovery key if err := c.seal.VerifyRecoveryKey(masterKey); err != nil { c.logger.Printf("[ERR] core: rekey aborted, recovery key verification failed: %v", err) return nil, err } // Generate a new master key newMasterKey, err := c.barrier.GenerateKey() if err != nil { c.logger.Printf("[ERR] core: failed to generate recovery key: %v", err) return nil, fmt.Errorf("recovery key generation failed: %v", err) } // Return the master key if only a single key part is used results := &RekeyResult{ Backup: c.recoveryRekeyConfig.Backup, } if c.recoveryRekeyConfig.SecretShares == 1 { results.SecretShares = append(results.SecretShares, newMasterKey) } else { // Split the master key using the Shamir algorithm shares, err := shamir.Split(newMasterKey, c.recoveryRekeyConfig.SecretShares, c.recoveryRekeyConfig.SecretThreshold) if err != nil { c.logger.Printf("[ERR] core: failed to generate shares: %v", err) return nil, fmt.Errorf("failed to generate shares: %v", err) } results.SecretShares = shares } if len(c.recoveryRekeyConfig.PGPKeys) > 0 { hexEncodedShares := make([][]byte, len(results.SecretShares)) for i, _ := range results.SecretShares { hexEncodedShares[i] = []byte(hex.EncodeToString(results.SecretShares[i])) } results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.recoveryRekeyConfig.PGPKeys) if err != nil { return nil, err } if c.recoveryRekeyConfig.Backup { backupInfo := map[string][]string{} for i := 0; i < len(results.PGPFingerprints); i++ { encShare := bytes.NewBuffer(results.SecretShares[i]) if backupInfo[results.PGPFingerprints[i]] == nil { backupInfo[results.PGPFingerprints[i]] = []string{hex.EncodeToString(encShare.Bytes())} } else { backupInfo[results.PGPFingerprints[i]] = append(backupInfo[results.PGPFingerprints[i]], hex.EncodeToString(encShare.Bytes())) } } backupVals := &RekeyBackup{ Nonce: c.recoveryRekeyConfig.Nonce, Keys: backupInfo, } buf, err := json.Marshal(backupVals) if err != nil { c.logger.Printf("[ERR] core: failed to marshal recovery key backup: %v", err) return nil, fmt.Errorf("failed to marshal recovery key backup: %v", err) } pe := &physical.Entry{ Key: coreRecoveryUnsealKeysBackupPath, Value: buf, } if err = c.physical.Put(pe); err != nil { c.logger.Printf("[ERR] core: failed to save unseal key backup: %v", err) return nil, fmt.Errorf("failed to save unseal key backup: %v", err) } } } if err := c.seal.SetRecoveryKey(newMasterKey); err != nil { c.logger.Printf("[ERR] core: failed to set recovery key: %v", err) return nil, fmt.Errorf("failed to set recovery key: %v", err) } if err := c.seal.SetRecoveryConfig(c.recoveryRekeyConfig); err != nil { c.logger.Printf("[ERR] core: error saving rekey seal configuration: %v", err) return nil, fmt.Errorf("failed to save rekey seal configuration: %v", err) } // Done! c.recoveryRekeyProgress = nil c.recoveryRekeyConfig = nil return results, nil }
// Initialize is used to initialize the Vault with the given // configurations. func (c *Core) Initialize(initParams *InitParams) (*InitResult, error) { barrierConfig := initParams.BarrierConfig recoveryConfig := initParams.RecoveryConfig if c.seal.RecoveryKeySupported() { if recoveryConfig == nil { return nil, fmt.Errorf("recovery configuration must be supplied") } if recoveryConfig.SecretShares < 1 { return nil, fmt.Errorf("recovery configuration must specify a positive number of shares") } // Check if the seal configuration is valid if err := recoveryConfig.Validate(); err != nil { c.logger.Error("core: invalid recovery configuration", "error", err) return nil, fmt.Errorf("invalid recovery configuration: %v", err) } } // Check if the seal configuration is valid if err := barrierConfig.Validate(); err != nil { c.logger.Error("core: invalid seal configuration", "error", err) return nil, fmt.Errorf("invalid seal configuration: %v", err) } // Avoid an initialization race c.stateLock.Lock() defer c.stateLock.Unlock() // Check if we are initialized init, err := c.Initialized() if err != nil { return nil, err } if init { return nil, ErrAlreadyInit } err = c.seal.Init() if err != nil { c.logger.Error("core: failed to initialize seal", "error", err) return nil, fmt.Errorf("error initializing seal: %v", err) } err = c.seal.SetBarrierConfig(barrierConfig) if err != nil { c.logger.Error("core: failed to save barrier configuration", "error", err) return nil, fmt.Errorf("barrier configuration saving failed: %v", err) } barrierKey, barrierUnsealKeys, err := c.generateShares(barrierConfig) if err != nil { c.logger.Error("core: error generating shares", "error", err) return nil, err } // If we are storing shares, pop them out of the returned results and push // them through the seal if barrierConfig.StoredShares > 0 { var keysToStore [][]byte for i := 0; i < barrierConfig.StoredShares; i++ { keysToStore = append(keysToStore, barrierUnsealKeys[0]) barrierUnsealKeys = barrierUnsealKeys[1:] } if err := c.seal.SetStoredKeys(keysToStore); err != nil { c.logger.Error("core: failed to store keys", "error", err) return nil, fmt.Errorf("failed to store keys: %v", err) } } results := &InitResult{ SecretShares: barrierUnsealKeys, } // Initialize the barrier if err := c.barrier.Initialize(barrierKey); err != nil { c.logger.Error("core: failed to initialize barrier", "error", err) return nil, fmt.Errorf("failed to initialize barrier: %v", err) } if c.logger.IsInfo() { c.logger.Info("core: security barrier initialized", "shares", barrierConfig.SecretShares, "threshold", barrierConfig.SecretThreshold) } // Unseal the barrier if err := c.barrier.Unseal(barrierKey); err != nil { c.logger.Error("core: failed to unseal barrier", "error", err) return nil, fmt.Errorf("failed to unseal barrier: %v", err) } // Ensure the barrier is re-sealed defer func() { if err := c.barrier.Seal(); err != nil { c.logger.Error("core: failed to seal barrier", "error", err) } }() // Perform initial setup if err := c.setupCluster(); err != nil { c.stateLock.Unlock() c.logger.Error("core: cluster setup failed during init", "error", err) return nil, err } if err := c.postUnseal(); err != nil { c.logger.Error("core: post-unseal setup failed during init", "error", err) return nil, err } // Save the configuration regardless, but only generate a key if it's not // disabled. When using recovery keys they are stored in the barrier, so // this must happen post-unseal. if c.seal.RecoveryKeySupported() { err = c.seal.SetRecoveryConfig(recoveryConfig) if err != nil { c.logger.Error("core: failed to save recovery configuration", "error", err) return nil, fmt.Errorf("recovery configuration saving failed: %v", err) } if recoveryConfig.SecretShares > 0 { recoveryKey, recoveryUnsealKeys, err := c.generateShares(recoveryConfig) if err != nil { c.logger.Error("core: failed to generate recovery shares", "error", err) return nil, err } err = c.seal.SetRecoveryKey(recoveryKey) if err != nil { return nil, err } results.RecoveryShares = recoveryUnsealKeys } } // Generate a new root token rootToken, err := c.tokenStore.rootToken() if err != nil { c.logger.Error("core: root token generation failed", "error", err) return nil, err } results.RootToken = rootToken.ID c.logger.Info("core: root token generated") if initParams.RootTokenPGPKey != "" { _, encryptedVals, err := pgpkeys.EncryptShares([][]byte{[]byte(results.RootToken)}, []string{initParams.RootTokenPGPKey}) if err != nil { c.logger.Error("core: root token encryption failed", "error", err) return nil, err } results.RootToken = base64.StdEncoding.EncodeToString(encryptedVals[0]) } // Prepare to re-seal if err := c.preSeal(); err != nil { c.logger.Error("core: pre-seal teardown failed", "error", err) return nil, err } return results, nil }