AwsEncryptionSdk.Stream.Encryptor (AWS Encryption SDK v0.7.0)

View Source

Streaming encryptor state machine for incremental plaintext processing.

When to Use Streaming

Use Stream.Encryptor instead of Client.encrypt/2 when:

  • Encrypting large files that don't fit in memory
  • Processing data from network streams or pipes
  • Working with data sources that produce chunks incrementally
  • Memory constraints require bounded memory usage

For small messages (< 1MB), the simpler Client.encrypt/2 API is recommended.

Memory Efficiency

The streaming encryptor maintains constant memory usage regardless of input size:

  • Buffers only one frame's worth of plaintext (default: 4096 bytes)
  • Emits ciphertext incrementally as complete frames
  • No need to load entire plaintext into memory

For a 1GB file with 4KB frames, memory usage is ~4KB regardless of file size, compared to ~1GB for non-streaming encryption.

Integration with Elixir Streams

Designed to work seamlessly with Stream module:

File.stream!("large-file.bin", [], 4096)
|> AwsEncryptionSdk.Stream.encrypt(client)
|> Stream.into(File.stream!("output.encrypted"))
|> Stream.run()

See AwsEncryptionSdk.Stream for high-level streaming API.

State Machine

The encryptor progresses through these states:

  1. :init - Not started, awaiting first input
  2. :encrypting - Processing frames
  3. :done - Encryption complete

Low-Level Example

For custom streaming logic, use the state machine directly:

# Initialize encryptor
{:ok, enc} = Encryptor.init(materials, frame_length: 4096)

# Process chunks, collecting output
{:ok, enc, header_bytes} = Encryptor.start(enc)
{:ok, enc, frame1_bytes} = Encryptor.update(enc, chunk1)
{:ok, enc, frame2_bytes} = Encryptor.update(enc, chunk2)
{:ok, enc, final_bytes} = Encryptor.finalize(enc)

# Concatenate: header_bytes <> frame1_bytes <> frame2_bytes <> final_bytes

See Also

Summary

Functions

Finalizes encryption.

Initializes a new streaming encryptor.

Starts encryption by generating header.

Returns the current state of the encryptor.

Processes plaintext chunk.

Types

state()

@type state() :: :init | :encrypting | :done

t()

@type t() :: %AwsEncryptionSdk.Stream.Encryptor{
  buffer: binary(),
  derived_key: binary() | nil,
  frame_length: pos_integer(),
  header: AwsEncryptionSdk.Format.Header.t() | nil,
  materials: AwsEncryptionSdk.Materials.EncryptionMaterials.t(),
  sequence_number: pos_integer(),
  signature_acc: AwsEncryptionSdk.Stream.SignatureAccumulator.t() | nil,
  state: state()
}

Functions

finalize(enc)

@spec finalize(t()) :: {:ok, t(), binary()} | {:error, term()}

Finalizes encryption.

Encrypts any remaining buffered data as the final frame, optionally adds footer. Returns {:ok, updated_encryptor, final_bytes}.

init(materials, opts \\ [])

@spec init(
  AwsEncryptionSdk.Materials.EncryptionMaterials.t(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Initializes a new streaming encryptor.

Options

  • :frame_length - Frame size in bytes (default: 4096)

start(enc)

@spec start(t()) :: {:ok, t(), binary()} | {:error, term()}

Starts encryption by generating header.

Returns {:ok, updated_encryptor, header_bytes} on success. Must be called before update/2.

state(encryptor)

@spec state(t()) :: state()

Returns the current state of the encryptor.

update(enc, plaintext)

@spec update(t(), binary()) :: {:ok, t(), binary()} | {:error, term()}

Processes plaintext chunk.

Buffers partial frames and emits complete frames. Returns {:ok, updated_encryptor, frame_bytes} where frame_bytes may be empty if not enough data for a complete frame.