func TestDerivedKeyUpgrade(t *testing.T) { storage := &logical.InmemStorage{} key, _ := uuid.GenerateRandomBytes(32) context, _ := uuid.GenerateRandomBytes(32) p := &policy{ Name: "test", Key: key, Type: keyType_AES256_GCM96, Derived: true, } p.migrateKeyToKeysMap() p.upgrade(storage) // Need to run the upgrade code to make the migration stick if p.KDF != kdf_hmac_sha256_counter { t.Fatalf("bad KDF value by default; counter val is %d, KDF val is %d, policy is %#v", kdf_hmac_sha256_counter, p.KDF, *p) } derBytesOld, err := p.DeriveKey(context, 1) if err != nil { t.Fatal(err) } derBytesOld2, err := p.DeriveKey(context, 1) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(derBytesOld, derBytesOld2) { t.Fatal("mismatch of same context alg") } p.KDF = kdf_hkdf_sha256 if p.needsUpgrade() { t.Fatal("expected no upgrade needed") } derBytesNew, err := p.DeriveKey(context, 1) if err != nil { t.Fatal(err) } derBytesNew2, err := p.DeriveKey(context, 1) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(derBytesNew, derBytesNew2) { t.Fatal("mismatch of same context alg") } if reflect.DeepEqual(derBytesOld, derBytesNew) { t.Fatal("match of different context alg") } }
func (p *policy) upgrade(storage logical.Storage) error { persistNeeded := false // Ensure we've moved from Key -> Keys if p.Key != nil && len(p.Key) > 0 { p.migrateKeyToKeysMap() persistNeeded = true } // With archiving, past assumptions about the length of the keys map are no // longer valid if p.LatestVersion == 0 && len(p.Keys) != 0 { p.LatestVersion = len(p.Keys) persistNeeded = true } // We disallow setting the version to 0, since they start at 1 since moving // to rotate-able keys, so update if it's set to 0 if p.MinDecryptionVersion == 0 { p.MinDecryptionVersion = 1 persistNeeded = true } // On first load after an upgrade, copy keys to the archive if p.ArchiveVersion == 0 { persistNeeded = true } if p.ConvergentEncryption && p.ConvergentVersion == 0 { p.ConvergentVersion = 1 persistNeeded = true } if p.Keys[p.LatestVersion].HMACKey == nil || len(p.Keys[p.LatestVersion].HMACKey) == 0 { entry := p.Keys[p.LatestVersion] hmacKey, err := uuid.GenerateRandomBytes(32) if err != nil { return err } entry.HMACKey = hmacKey p.Keys[p.LatestVersion] = entry persistNeeded = true } if persistNeeded { err := p.Persist(storage) if err != nil { return err } } return nil }
func (b *backend) pathRandomWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { bytes := 0 var err error strBytes := d.Get("urlbytes").(string) if strBytes != "" { bytes, err = strconv.Atoi(strBytes) if err != nil { return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil } } else { bytes = d.Get("bytes").(int) } format := d.Get("format").(string) if bytes < 1 { return logical.ErrorResponse(`"bytes" cannot be less than 1`), nil } switch format { case "hex": case "base64": default: return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"hex\" or \"base64\"", format)), nil } randBytes, err := uuid.GenerateRandomBytes(bytes) if err != nil { return nil, err } var retStr string switch format { case "hex": retStr = hex.EncodeToString(randBytes) case "base64": retStr = base64.StdEncoding.EncodeToString(randBytes) } // Generate the response resp := &logical.Response{ Data: map[string]interface{}{ "random_bytes": retStr, }, } return resp, nil }
func TestKeyUpgrade(t *testing.T) { key, _ := uuid.GenerateRandomBytes(32) p := &policy{ Name: "test", Key: key, Type: keyType_AES256_GCM96, } p.migrateKeyToKeysMap() if p.Key != nil || p.Keys == nil || len(p.Keys) != 1 || !reflect.DeepEqual(p.Keys[1].AESKey, key) { t.Errorf("bad key migration, result is %#v", p.Keys) } }
func (p *policy) rotate(storage logical.Storage) error { if p.Keys == nil { // This is an initial key rotation when generating a new policy. We // don't need to call migrate here because if we've called getPolicy to // get the policy in the first place it will have been run. p.Keys = keyEntryMap{} } p.LatestVersion += 1 entry := keyEntry{ CreationTime: time.Now().Unix(), } hmacKey, err := uuid.GenerateRandomBytes(32) if err != nil { return err } entry.HMACKey = hmacKey switch p.Type { case keyType_AES256_GCM96: // Generate a 256bit key newKey, err := uuid.GenerateRandomBytes(32) if err != nil { return err } entry.AESKey = newKey case keyType_ECDSA_P256: privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return err } entry.EC_D = privKey.D entry.EC_X = privKey.X entry.EC_Y = privKey.Y derBytes, err := x509.MarshalPKIXPublicKey(privKey.Public()) if err != nil { return fmt.Errorf("error marshaling public key: %s", err) } pemBlock := &pem.Block{ Type: "PUBLIC KEY", Bytes: derBytes, } pemBytes := pem.EncodeToMemory(pemBlock) if pemBytes == nil || len(pemBytes) == 0 { return fmt.Errorf("error PEM-encoding public key") } entry.FormattedPublicKey = string(pemBytes) } p.Keys[p.LatestVersion] = entry // This ensures that with new key creations min decryption version is set // to 1 rather than the int default of 0, since keys start at 1 (either // fresh or after migration to the key map) if p.MinDecryptionVersion == 0 { p.MinDecryptionVersion = 1 } return p.Persist(storage) }
func (p *policy) Encrypt(context, nonce []byte, value string) (string, error) { if !p.Type.EncryptionSupported() { return "", errutil.UserError{Err: fmt.Sprintf("message encryption not supported for key type %v", p.Type)} } // Guard against a potentially invalid key type switch p.Type { case keyType_AES256_GCM96: default: return "", errutil.InternalError{Err: fmt.Sprintf("unsupported key type %v", p.Type)} } // Decode the plaintext value plaintext, err := base64.StdEncoding.DecodeString(value) if err != nil { return "", errutil.UserError{Err: "failed to base64-decode plaintext"} } // Derive the key that should be used key, err := p.DeriveKey(context, p.LatestVersion) if err != nil { return "", err } // Guard against a potentially invalid key type switch p.Type { case keyType_AES256_GCM96: default: return "", errutil.InternalError{Err: fmt.Sprintf("unsupported key type %v", p.Type)} } // Setup the cipher aesCipher, err := aes.NewCipher(key) if err != nil { return "", errutil.InternalError{Err: err.Error()} } // Setup the GCM AEAD gcm, err := cipher.NewGCM(aesCipher) if err != nil { return "", errutil.InternalError{Err: err.Error()} } if p.ConvergentEncryption { switch p.ConvergentVersion { case 1: if len(nonce) != gcm.NonceSize() { return "", errutil.UserError{Err: fmt.Sprintf("base64-decoded nonce must be %d bytes long when using convergent encryption with this key", gcm.NonceSize())} } default: nonceHmac := hmac.New(sha256.New, context) nonceHmac.Write(plaintext) nonceSum := nonceHmac.Sum(nil) nonce = nonceSum[:gcm.NonceSize()] } } else { // Compute random nonce nonce, err = uuid.GenerateRandomBytes(gcm.NonceSize()) if err != nil { return "", errutil.InternalError{Err: err.Error()} } } // Encrypt and tag with GCM out := gcm.Seal(nil, nonce, plaintext, nil) // Place the encrypted data after the nonce full := out if !p.ConvergentEncryption || p.ConvergentVersion > 1 { full = append(nonce, out...) } // Convert to base64 encoded := base64.StdEncoding.EncodeToString(full) // Prepend some information encoded = "vault:v" + strconv.Itoa(p.LatestVersion) + ":" + encoded return encoded, nil }