import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"golang.org/x/crypto/hkdf"
)
const (
hkdfInfo = "api-key-rotation"
versionLen = 1
saltLen = 32
nonceLen = 12
)
func decryptCredentials(secretKey, encryptedB64 string) (map[string]string, error) {
raw, err := base64.StdEncoding.DecodeString(encryptedB64)
if err != nil {
raw, err = base64.RawStdEncoding.DecodeString(encryptedB64)
if err != nil {
return nil, fmt.Errorf("base64 decode: %w", err)
}
}
if raw[0] != 1 {
return nil, fmt.Errorf("unsupported wire version: %d", raw[0])
}
salt := raw[versionLen : versionLen+saltLen]
nonce := raw[versionLen+saltLen : versionLen+saltLen+nonceLen]
ciphertext := raw[versionLen+saltLen+nonceLen:]
aesKey := make([]byte, 32)
r := hkdf.New(sha256.New, []byte(secretKey), salt, []byte(hkdfInfo))
if _, err := io.ReadFull(r, aesKey); err != nil {
return nil, fmt.Errorf("hkdf: %w", err)
}
block, err := aes.NewCipher(aesKey)
if err != nil {
return nil, fmt.Errorf("aes: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("gcm: %w", err)
}
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("decrypt: %w", err)
}
var creds map[string]string
if err := json.Unmarshal(plaintext, &creds); err != nil {
return nil, fmt.Errorf("json: %w", err)
}
return creds, nil
}
// Usage
creds, err := decryptCredentials(SECRET_KEY, response.EncryptedCredentials)
// Store creds["access_key"], creds["secret_key"], and creds["passphrase"]