Skip to content

Latest commit

 

History

History
92 lines (64 loc) · 3.48 KB

Specification.md

File metadata and controls

92 lines (64 loc) · 3.48 KB

Sapient Specification

Overview

Sapient provides the following operations that can be performed on HTTP requests and responses:

  • Shared-Key (Symmetric) Cryptography
    1. Authenticate / Verify
    2. Encrypt / Decrypt
  • Public-Key (Asymmetric) Cryptography
    1. Sign / Verify
    2. Seal / Unseal

Operations

Shared-key Authentication

Messages are authenticated with HMAC-SHA512256, as provided by NaCl.

HMAC-SHA512256 is the leftmost 32 bytes of a raw binary string generated by HMAC-SHA512.

Sapient encodes the MAC with base64url, then stores it in a Body-HMAC-SHA512256 header. The HTTP message body is untouched by this operation.

Shared-Key Encyption

Messages are encrypted with XChaCha20-Poly1305, with a random 24-byte nonce.

The nonce must be generated from the operating system's non-blocking CSPRNG (i.e. /dev/urandom); userspace CSPRNGs such as OpenSSL are forbidden. Blocking randomness providers (/dev/random on old Linux kernels) are also forbidden.

After encryption, the nonce is prepended to the ciphertext. The encrypted message (in raw binary) is formatted like this.

nonce (24 bytes) || ciphertext (0 or more bytes) || tag (16 bytes)

Ciphertexts are base64url-encoded in transmission.

Public-Key Signatures

Messages are authenticated with Ed25519, as provided by NaCl and defined in RFC 8032.

Sapient encodes the signature with base64url, then stores it in a Body-Signature-Ed25519 header. The HTTP message body is untouched by this operation.

Public-Key Encyption (Sealing)

Each message encryption generates an ephemeral X25519 keypair. The ephemeral public key will be prepended to the ciphertext so that the shared secret key can be recalculated by a recipient with the correct X25519 signing key.

The derived key will be the first 32 bytes of a 56-byte BLAKE2b hash of the X25519 shared secret and both public keys. The nonce for the message will be the remaining 24 bytes.

The message is then encrypted with XChaCha20-Poly1305, with the ephemeral public key prepended to the message (and used as addition data). The encrypted message (in raw binary) is formatted like this.

Messages are encrypted with XChaCha20-Poly1305, with a random 24-byte nonce.

ephPublicKey (32 bytes) || ciphertext (0 or more bytes) || tag (16 bytes)

The process for encrypting a message with a recipient's public key (recipPublic) is as follows:

  1. Generate an ephemeral X25519 keypair (ephSecret, ephPublic)
  2. Calculate X25519(ephSecret, recipPublic)
  3. Calculate BLAKE2b-448(stepTwo || ephPublic || recipPublic)
  4. The first 32 bytes of the output of Step 3 is the key.
  5. The remaining 24 bytes of the output of Step 3 is the nonce.
  6. Encrypt the message, using XChaCha20-Poly1305.
    • The additional data is the ephemeral public key.

The process for decrypting a sealed message is as follows:

  1. Separate the ephPublicKey from the rest of the ciphertext.
  2. Calculate X25519(recipSecret, ephPublic)
  3. Calculate BLAKE2b-448(stepTwo || ephPublic || recipPublic)
  4. The first 32 bytes of the output of Step 3 is the key.
  5. The remaining 24 bytes of the output of Step 3 is the nonce.
  6. Decrypt the message, using XChaCha20-Poly1305.
    • The additional data is the ephemeral public key.

Ciphertexts are base64url-encoded in transmission.