Decrypt with AWS KMS¶
Recover a session key where the private half lives in KMS and never leaves it.
What you need¶
- An
ECC_NIST_P256,ECC_NIST_P384orECC_NIST_P521KMS key whoseKeyUsageisKEY_AGREEMENT. Those are the only specs KMS offers for key agreement, and RSA cannot be substituted — see why ECDH, not RSA. - Credentials that can call
kms:GetPublicKeyandkms:DeriveSharedSecreton it. - The certificate the message was addressed to, for its KDF parameters.
Build a deriver¶
import (
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/kms"
kmsenc "gitlab.com/phpboyscout/go/encryption-aws-kms"
)
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("eu-west-2"))
if err != nil {
return err
}
client := kms.NewFromConfig(cfg)
deriver, err := kmsenc.NewDeriverForKey(ctx, client, client, "alias/your-key")
if err != nil {
return err
}
Prefer NewDeriverForKey. It reads the curve from the key and refuses a key
that cannot perform ECDH, so a misconfiguration fails at start-up rather than
per message. NewDeriver takes the curve as an argument for callers who already
know it, and a constant that disagrees with the key produces an error naming the
sender's point — which sends whoever debugs it in the wrong direction.
Recover the session key¶
secret, err := deriver.DeriveSharedSecret(ctx, pkesk.EphemeralPoint)
if err != nil {
return err
}
cipherID, sessionKey, err := encryption.SessionKey(encryption.Message{
Version: pkesk.Version,
SharedSecret: secret,
WrappedKey: pkesk.WrappedKey,
}, encryption.KDFParams{
CurveOID: curveOID, // from the certificate, with its length octet
Fingerprint: fingerprint, // of the encryption subkey
HashID: hashID, // from the certificate
KEKAlgID: kekAlgID, // from the certificate
CoordinateBytes: deriver.CoordinateBytes(),
})
Take the KDF parameters from the certificate the sender used, not from constants. The derivation binds the subkey's fingerprint, so parameters that do not come from that certificate cannot recover anything.
What the errors mean¶
The distinction matters when you are the one being paged:
| Error | Meaning |
|---|---|
ErrIntegrity |
The message is not addressed to this key. |
ErrChecksum |
The key was right; the payload under it is wrong. |
ErrCurveMismatch |
The key behind the alias is not the curve configured. |
ErrKeyRotated |
The alias resolved to a different key than last time. |
ErrNotConfigured |
A wiring fault — a missing client, an empty key id. |
ErrKeyRotated is the one worth wiring an alert to. It means messages addressed
to the old certificate will stop opening, and no amount of retrying will help.
Cost and permissions¶
One DeriveSharedSecret call per message. NewDeriverForKey adds one
GetPublicKey call at construction, so build the deriver once and reuse it
rather than per message.