Skip to content

Repository files navigation

pgEnvelope

CI License PostgreSQL

Enterprise-grade envelope encryption for PostgreSQL with Rust-powered KMS integrations

pgEnvelope is a PostgreSQL extension that provides modern, secure, KMS-backed envelope encryption for database columns. It uses a Rust cryptographic core for modern security guarantees and a lightweight C shim to integrate seamlessly with PostgreSQL.

pgEnvelope brings PostgreSQL into a world where encrypted-at-rest is not enough. Instead, each piece of sensitive data receives its own envelope encryption key, automatically managed through an enterprise KMS or HSM.


✨ Features

πŸ” Envelope Encryption (AEAD)

  • Authenticated encryption using modern AEAD (ChaCha20-Poly1305 in the local core)
  • Automatic nonce generation
  • Strict integrity protection
  • Zero unsafe cryptography in Cβ€”only in Rust

πŸ”‘ Pluggable KMS / HSM Providers

pgEnvelope currently supports:

Provider Status Notes
LocalKMS βœ… Ready Development & testing mode
AWS KMS βœ… Ready Uses AWS SDK; supports any region
Azure Key Vault βœ… Ready Access token retrieval with Key Vault mock support
Google Cloud KMS βš™οΈ Functional Requires explicit env configuration
Hashicorp Vault βœ… Ready Supports transit engine
SoftHSM2 / PKCS#11 βš™οΈ Functional Key slots tested, token initialization supported

Each provider is implemented inside the Rust core and selected by prefix (e.g., aws:mykey, vault:mykey).

🧱 Ciphertext formats (v2 inline, v3 legacy envelope, v4 envelope)

pgEnvelope supports three compatible ciphertext formats:

  1. Inline v2 (legacy inline):
   | version (2) | scheme_id | nonce | ciphertext+tag |

Used by older schemes like local_default, vault_default, and early AWS integrations.

Still fully supported for decryption and migration.

  1. Envelope v3 (legacy envelope):
| version (3) | provider scheme_id | envelope_header | wrapped_dek | ciphertext+tag |

Legacy envelope format still supported for decryption/rewrap.

  1. Envelope v4 (current, recommended):
| version (4) | provider_scheme_id | logical_scheme_id | envelope_header | wrapped_dek | ciphertext+tag |

The envelope_header includes:

  • provider_scheme_id β€” backend KMS family (local, Vault, AWS, …)
  • logical_scheme_id β€” versioned scheme identifier (from pgenvelope_schemes)
  • algo β€” local AEAD algorithm (e.g. chacha20poly1305)
  • nonce β€” per-message nonce for the local AEAD
  • dek_id / dek_version β€” identity of the data-encryption key
  • master_key_alias β€” KMS alias used to wrap this DEK

In v4, the payload is always encrypted using a local AEAD key (DEK), and the DEK is then wrapped by the KMS. This gives you:

  • strong separation of concerns (local crypto vs. remote KMS)
  • cheap rotations via DEK rewrap
  • a single, stable format across providers (local, Vault, AWS, …)

You can inspect any ciphertext via:

SELECT *
FROM pgenvelope_inspect_ciphertext(<ciphertext_bytea>);

which exposes version, scheme_id (logical), algorithm, master_key_alias, wrapped_dek_len, and more.

Envelope-mode schemes

On top of the raw providers, pgEnvelope ships with envelope-mode schemes:

Scheme name Backend provider Ciphertext version Notes
local_envelope LocalKMS v4 envelope Dev / test envelope-mode using local AEAD
vault_envelope Vault Transit v4 envelope Production-style envelope using Vault KMS
aws_envelope AWS KMS v4 envelope AWS-backed envelope mode

These schemes all use the same v4 envelope format and differ only in how the DEK is wrapped (local, Vault Transit, AWS KMS, …). Legacy v3 envelope ciphertexts remain decryptable.

πŸ”„ Key Rotation

pgEnvelope supports:

  • Manual rotation and rewrap (reencrypt column / rotate scheme)
  • Transparent decryption with legacy ciphertext formats
  • Auditable rotation logs and dry-run planning

πŸ§ͺ Regression-tested SQL API

SQL interface functions include:

  • pgenvelope_register_column(regclass, text, text)
  • pgenvelope_encrypt_for_column(regclass, text, anyelement)
  • pgenvelope_decrypt_for_column(regclass, text, bytea)
  • pgenvelope_encrypt_for_column_typed(regclass, text, anyelement)
  • pgenvelope_decrypt_for_column_typed(regclass, text, bytea, regtype)
  • Typed convenience wrappers: pgenvelope_decrypt_for_column_typed_text/int2/int4/int8/bool/uuid/float4/float8/time/timetz/timestamp/timestamptz/date/interval/json/jsonb/jsonpath/bytea/inet/cidr/macaddr/macaddr8/tsvector/tsquery/numeric
  • pgenvelope_list_schemes()
  • pgenvelope_kms_status()
  • pgenvelope_rotate_scheme* / pgenvelope_rewrap_*
  • pgenvelope_inspect_ciphertext(bytea)

Regression tests are included and run during CI.

🧱 Architecture

PostgreSQL 13–18
        β”‚
        β–Ό
  C Extension Shim (pgenvelope.so)
        β”‚ FFI
        β–Ό
Rust Core Library (libpgenvelope_core.so)
   β”œβ”€β”€ KMS Providers
   β”œβ”€β”€ ChaCha20-Poly1305 (local AEAD)
   β”œβ”€β”€ Key Wrapping / Unwrapping
   └── Envelope Format + Rotation Helpers

πŸš€ Quickstart (Docker Compose)

This repo includes a full dev stack (Postgres + Vault + LocalStack + Azure mock + optional SoftHSM2).

  1. Start the stack:
docker compose -f docker-compose.yml -f docker-compose.override.yml up -d --build
  1. Run the full test suite:
make test-all
  1. Try it manually:
psql -h localhost -U pgenvelope -d pgenvelopedb
CREATE EXTENSION pgenvelope;

CREATE TABLE public.quickstart (
  id bigserial primary key,
  data_plain text,
  data_enc bytea
);

SELECT pgenvelope_register_column('public.quickstart'::regclass, 'data_enc', 'local_default');

INSERT INTO public.quickstart (data_plain, data_enc)
VALUES ('hello', pgenvelope_encrypt_for_column('public.quickstart'::regclass, 'data_enc', 'hello'::text));

SELECT * FROM pgenvelope_inspect_ciphertext((SELECT data_enc FROM public.quickstart LIMIT 1));

πŸ“¦ Supported PostgreSQL Versions

pgEnvelope builds against:

PostgreSQL 13, 14, 15, 16, 17, 18

The same source code is used for all versions. The extension must be compiled separately for each major version using that version’s pg_config.


πŸ“₯ Installation

pgEnvelope can be installed:

  • via Docker (recommended)
  • manually from source
  • inside CI/CD pipelines for packaging or testing

Full instructions are available in: πŸ‘‰ docs/Building and Installing.md

Summary below.


πŸ”§ 1. Build Rust Core

cd rustcore
cargo build --release

Produces:

rustcore/target/release/libpgenvelope_core.so

πŸ› οΈ 2. Build the PostgreSQL Extension (C shim)

Example: for PostgreSQL 16

cd cshim
make PG_CONFIG=/usr/lib/postgresql/16/bin/pg_config
sudo make install PG_CONFIG=/usr/lib/postgresql/16/bin/pg_config

Copy Rust core into PostgreSQL’s library directory:

sudo cp ../rustcore/target/release/libpgenvelope_core.so $(pg_config --libdir)/

πŸ—„ 3. Enable the Extension in PostgreSQL

CREATE
EXTENSION pgenvelope;

🐳 Docker Usage

pgEnvelope provides a multi-version Docker build system. Build an image for the desired PostgreSQL version:

docker build --build-arg PG_MAJOR=16 -t pgenvelope-pg16 .
docker build --build-arg PG_MAJOR=15 -t pgenvelope-pg15 .
docker build --build-arg PG_MAJOR=14 -t pgenvelope-pg14 .
docker build --build-arg PG_MAJOR=13 -t pgenvelope-pg13 .

Start a pgEnvelope-enabled database:

docker run -p 5432:5432 --name pgenvelope16 pgenvelope-pg16

Inside the container:

CREATE
EXTENSION pgenvelope;

The repo also contains ready-made docker-compose environments including Vault / SoftHSM2 setups.


πŸ”§ Configuration

pgEnvelope configures KMS providers via environment variables.

See detailed configuration instructions: πŸ‘‰ docs/KMS-Configuration.md


πŸ§ͺ Running Tests

SQL regression tests (PGXS):

cd cshim
make PG_CONFIG=/usr/lib/postgresql/16/bin/pg_config installcheck

Rust tests:

cd rustcore
cargo test

Full stack (Docker + SQL + Rust):

make test-all

πŸ“š Documentation

  • docs/Building and Installing.md
  • docs/KMS-Configuration.md
  • docs/Type Support.md
  • docs/Security Model Overview.md
  • docs/Operations Cookbook.md
  • docs/Operations Incident Runbook.md
  • docs/Scheme-Model-Decision.md

For exclusions and pseudo-type notes, see Not applicable / not supported in docs/Type Support.md.


🧩 Directory Structure

pgEnvelope/
β”‚
β”œβ”€β”€ rustcore/                 # Rust cryptographic engine + KMS providers
β”‚   └── src/
β”‚       β”œβ”€β”€ kms/              # AWS, Azure, Vault, LocalKms, SoftHSM2, GCP
β”‚       └── ffi.rs            # FFI boundary to PostgreSQL shim
β”‚
β”œβ”€β”€ cshim/                    # PostgreSQL extension C layer
β”‚   β”œβ”€β”€ pgenvelope.c
β”‚   β”œβ”€β”€ pgenvelope_ffi.h
β”‚   └── pgenvelope--1.0.sql
β”‚
β”œβ”€β”€ e2e/                      # Testcontainers-based E2E harness
β”‚
β”œβ”€β”€ docker/                   # Vault + SoftHSM2 dev environments
β”œβ”€β”€ docs/                     # Installation, KMS config, architecture docs
└── cshim/sql/                # SQL regression tests

πŸ“˜ Example Usage

Encrypting a value (column-aware)

-- Register an encrypted column
SELECT pgenvelope_register_column('public.my_table'::regclass, 'data_enc', 'local_default');

-- Encrypt using column-aware AAD binding
INSERT INTO public.my_table (data_plain, data_enc)
VALUES ('hello', pgenvelope_encrypt_for_column('public.my_table'::regclass, 'data_enc', 'hello'::text));

Encrypting with typed I/O (binary preferred, text fallback)

-- Typed API uses a binary wire format when available, otherwise text
INSERT INTO public.my_table (data_enc)
VALUES (pgenvelope_encrypt_for_column_typed('public.my_table'::regclass, 'data_enc', now()::timestamptz));

SELECT pgenvelope_decrypt_for_column_typed('public.my_table'::regclass,
                                           'data_enc',
                                           data_enc,
                                           'timestamptz')::timestamptz
FROM public.my_table
LIMIT 1;

Typed convenience wrappers (no explicit casts)

SELECT pgenvelope_decrypt_for_column_typed_text('public.my_table'::regclass, 'data_enc', data_enc)
FROM public.my_table;

SELECT pgenvelope_decrypt_for_column_typed_int4('public.my_table'::regclass, 'data_enc', data_enc)
FROM public.my_table;

Decrypting

SELECT pgenvelope_decrypt_for_column('public.my_table'::regclass, 'data_enc', data_enc)
FROM my_table;

Automatic decryption view

SELECT pgenvelope_create_decrypted_view('public.my_table'::regclass);
SELECT * FROM public.my_table_dec;

πŸ›‘οΈ Security Guarantees

  • ChaCha20-Poly1305 authenticated encryption in the local core
  • Keys never stored unencrypted inside PostgreSQL
  • KMS / HSM performs all key-wrapping operations
  • FFI boundary kept minimal and memory-safe
  • Modern Rust crypto crates
  • Optional HSM-based root keys

πŸ“„ License

PPL


🀝 Contributing

We welcome PRs for:

  • New KMS / HSM providers (GCP KMS is next!)
  • More regression tests
  • Performance optimizations
  • Documentation improvements
  • Packaging (Deb/RPM)

⭐ If you like pgEnvelope, please star the repo.

It helps the project grow and encourages future development!

About

pgEnvelop is a KMS-Backed Column Encryption Toolkit for PostgreSQL

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages