Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package cryptor
// Package aes256gcm provides an AES-256-GCM [cryptor.Cryptor] implementation.
// All sensitive data is handled in mlock'd memory via securemem.
package aes256gcm

import (
"context"
Expand All @@ -8,6 +10,7 @@ import (
"errors"
"fmt"

"github.com/openkcm/krypton/internal/cryptor"
"github.com/openkcm/krypton/internal/securemem"
)

Expand All @@ -22,48 +25,55 @@ const (
// All key material and intermediate plaintext/ciphertext are handled in mlock'd
// memory via securemem to prevent leakage into swap or core dumps.
type AES256GCM struct {
info Info
info cryptor.Info
}

var _ Cryptor = &AES256GCM{}
var _ cryptor.Cryptor = &AES256GCM{}

// InfoNameAES256GCM indicates that the Cryptor supports AES-256 in Galois/Counter Mode (GCM).
const InfoNameAES256GCM cryptor.InfoName = "AES256-GCM"

// ErrAllocatedDataNotFound indicates that data reserved in the secure memory vault
// could not be retrieved after the cryptographic operation completed.
var ErrAllocatedDataNotFound = errors.New("allocated data not found in vault")

// NewAES256GCM returns a ready-to-use AES-256-GCM cryptor.
func NewAES256GCM() *AES256GCM {
// New returns a ready-to-use AES-256-GCM cryptor.
func New() *AES256GCM {
return &AES256GCM{
info: Info{
info: cryptor.Info{
Name: InfoNameAES256GCM,
DecryptionSecretRequired: true,
},
}
}

// Info returns metadata about the AES256GCM cryptor.
func (a *AES256GCM) Info() Info {
func (a *AES256GCM) Info() cryptor.Info {
return a.info
}

// Encrypt encrypts the plaintext using AES-256 in GCM mode with the provided key and AAD.
func (a *AES256GCM) Encrypt(ctx context.Context, req EncryptRequest) (*EncryptResponse, error) {
func (a *AES256GCM) Encrypt(ctx context.Context, req cryptor.EncryptRequest) (*cryptor.EncryptResponse, error) {
if err := req.Validate(); err != nil {
return nil, err
}

if req.Secret == nil {
return nil, fmt.Errorf("missing encryption secret: %w", ErrRequest)
return nil, fmt.Errorf("missing encryption secret: %w", cryptor.ErrRequest)
}

if req.Secret.Algorithm != cryptor.KeyAlgorithmAES256 {
return nil, fmt.Errorf("invalid key algorithm: expected %s, got %s: %w", cryptor.KeyAlgorithmAES256, req.Secret.Algorithm, cryptor.ErrRequest)
}

secretSize := len(req.Secret.SecureBytes())
secretSize := len(req.Secret.Data.SecureBytes())
if secretSize != 32 {
return nil, fmt.Errorf("invalid key size: expected 32 bytes, got %d: %w", secretSize, ErrRequest)
return nil, fmt.Errorf("invalid key size: expected 32 bytes, got %d: %w", secretSize, cryptor.ErrRequest)
}

resp, err := securemem.Run(ctx, func(ctx context.Context, hr *securemem.HandlerRequest) error {
// 1. Initialize AES-256 block cipher from the 32-byte key.
block, err := aes.NewCipher(req.Secret.SecureBytes())
block, err := aes.NewCipher(req.Secret.Data.SecureBytes())
if err != nil {
return fmt.Errorf("failed to create AES cipher: %w", err)
}
Expand Down Expand Up @@ -111,29 +121,33 @@ func (a *AES256GCM) Encrypt(ctx context.Context, req EncryptRequest) (*EncryptRe
return nil, fmt.Errorf("allocated ciphertext not found in vault after encryption: %w", ErrAllocatedDataNotFound)
}

return &EncryptResponse{
return &cryptor.EncryptResponse{
Ciphertext: cipherText,
}, nil
}

// Decrypt decrypts the ciphertext using AES-256 in GCM mode with the provided key and AAD.
func (a *AES256GCM) Decrypt(ctx context.Context, req DecryptRequest) (*DecryptResponse, error) {
func (a *AES256GCM) Decrypt(ctx context.Context, req cryptor.DecryptRequest) (*cryptor.DecryptResponse, error) {
if err := req.Validate(); err != nil {
return nil, err
}

if req.Secret == nil {
return nil, fmt.Errorf("missing decryption secret: %w", ErrRequest)
return nil, fmt.Errorf("missing decryption secret: %w", cryptor.ErrRequest)
}

if req.Secret.Algorithm != cryptor.KeyAlgorithmAES256 {
return nil, fmt.Errorf("invalid key algorithm: expected %s, got %s: %w", cryptor.KeyAlgorithmAES256, req.Secret.Algorithm, cryptor.ErrRequest)
}

secretSize := len(req.Secret.SecureBytes())
secretSize := len(req.Secret.Data.SecureBytes())
if secretSize != 32 {
return nil, fmt.Errorf("invalid key size: expected 32 bytes, got %d: %w", secretSize, ErrRequest)
return nil, fmt.Errorf("invalid key size: expected 32 bytes, got %d: %w", secretSize, cryptor.ErrRequest)
}

resp, err := securemem.Run(ctx, func(ctx context.Context, hr *securemem.HandlerRequest) error {
// 1. Initialize AES-256 block cipher from the 32-byte key.
block, err := aes.NewCipher(req.Secret.SecureBytes())
block, err := aes.NewCipher(req.Secret.Data.SecureBytes())
if err != nil {
return fmt.Errorf("failed to create AES cipher: %w", err)
}
Expand All @@ -149,7 +163,7 @@ func (a *AES256GCM) Decrypt(ctx context.Context, req DecryptRequest) (*DecryptRe

// 3. Verify the ciphertext is at least nonce + tag bytes long.
if cipherTextSize < nonceSize+gcm.Overhead() {
return fmt.Errorf("ciphertext too short: %w", ErrRequest)
return fmt.Errorf("ciphertext too short: %w", cryptor.ErrRequest)
}

// 4. Compute plaintext size: total - nonce - tag.
Expand Down Expand Up @@ -194,7 +208,7 @@ func (a *AES256GCM) Decrypt(ctx context.Context, req DecryptRequest) (*DecryptRe
return nil, fmt.Errorf("allocated plaintext not found in vault after decryption: %w", ErrAllocatedDataNotFound)
}

return &DecryptResponse{
return &cryptor.DecryptResponse{
Plaintext: plainText,
}, nil
}
Loading
Loading