Files
mpc/pkg/encryption/aes.go
T
zeekay 10dcad018e fix: address red team findings — auth, TLS, KMS fallback, AAD
M-1:  compose.yml commented section now enforces JWT_SECRET required syntax
M-3:  TLS client config uses TOFU certificate pinning instead of blind
      InsecureSkipVerify — SHA-256 fingerprint pinned on first connect,
      mismatches rejected on subsequent connections
M-4:  TLSOnlyListener plaintext escape hatch gated behind both
      ALLOW_PLAINTEXT=true AND ENVIRONMENT=development
M-5:  KMS fallback to unencrypted storage is now log.Fatal in production
      (ENVIRONMENT != "development"), including Put-time failures
M-6:  isKeyShare() now checks org: prefix, key-type prefixes (frost:,
      bls:, sr25519:, tfhe:), and hex wallet IDs instead of returning true
M-9:  AES-GCM Encrypt/Decrypt accept optional AAD variadic parameter
      for context-binding; existing callers remain backward compatible
M-12: Internal /keygen endpoint requires org_id in request body (400 if
      missing); orgID propagated to GenerateKeyMessage and audit log
2025-12-28 02:59:23 -08:00

71 lines
2.0 KiB
Go

package encryption
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
)
// EncryptAESGCM encrypts plaintext using AES-GCM with optional context-binding AAD.
// Key must be 16, 24, or 32 bytes for AES-128, AES-192, or AES-256.
// The aad parameter binds the ciphertext to a context (e.g. wallet_id or key path),
// preventing cross-context ciphertext transplant attacks. Pass nil for no AAD
// (backward compatible, but not recommended for new callers).
func EncryptAESGCM(plain, key []byte, aad ...[]byte) (ciphertext, nonce []byte, err error) {
// Validate key length for AES
switch len(key) {
case 16, 24, 32:
// Valid AES key sizes
default:
return nil, nil, fmt.Errorf("invalid AES key length: %d (must be 16, 24, or 32 bytes)", len(key))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, err
}
nonce = make([]byte, aead.NonceSize())
if _, err = rand.Read(nonce); err != nil {
return nil, nil, err
}
var additionalData []byte
if len(aad) > 0 {
additionalData = aad[0]
}
ciphertext = aead.Seal(nil, nonce, plain, additionalData)
return ciphertext, nonce, nil
}
// DecryptAESGCM decrypts ciphertext using AES-GCM with optional context-binding AAD.
// Key must be 16, 24, or 32 bytes for AES-128, AES-192, or AES-256.
// The aad parameter must match the AAD used during encryption; mismatched AAD
// causes authentication failure, preventing cross-context transplant attacks.
func DecryptAESGCM(ciphertext, key, nonce []byte, aad ...[]byte) ([]byte, error) {
// Validate key length for AES
switch len(key) {
case 16, 24, 32:
// Valid AES key sizes
default:
return nil, fmt.Errorf("invalid AES key length: %d (must be 16, 24, or 32 bytes)", len(key))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
var additionalData []byte
if len(aad) > 0 {
additionalData = aad[0]
}
return aead.Open(nil, nonce, ciphertext, additionalData)
}