Oboron Protocol Specification

1.0 draft-rev2 ()

Contents


1. Formats

An Oboron format represents the full transformation of the plaintext to the encrypted text (obtext), including:

  1. Encryption: Plaintext UTF-8 string encrypted to ciphertext bytes using a cryptographic algorithm
  2. Encoding: The binary payload is encoded to a string representation

1.1 Scheme + Encoding = Format

Formats combine a scheme (cryptographic algorithm) with an encoding (string representation):

Given an encryption key, the format thus uniquely specifies the complete transformation from a plaintext string to an encoded obtext string.

Formats are represented by identifiers:

API Notes:

1.2 Encodings

All encodings MUST produce output with no padding characters.

1.3 Schemes

Schemes define the encryption algorithm and its properties, classified into tiers:

Scheme Tiers

Scheme Properties

The second letter of the scheme ID further describes the properties of the scheme:

Prefix Restructuring

....x: Schemes with 5-letter identifiers have an x suffix, indicating that the underlying primitive's ciphertext is has been modified in order to achieve the referenceable prefix property.

This does not apply to a- or u-tier schemes, which either have the avalanche property (high entropy throughout, including prefix), or they are probabilistic (therefore not referenceable — referenceability implies determinism).

Currently, the only scheme using prefix restructuring is ob:zrbcx: AES-CBC, being a block-chaining algorithm, has high entropy in the tail, but weak entropy in the head, warranting prefix restructuring, detailed in Section 2.1.4 below.

Scheme Cryptographic Algorithms

The remaining two letters in scheme IDs indicate the algorithm:

Summary Table

Scheme Algorithm Deterministic? Authenticated? Notes
ob:aasv AES-SIV Yes Yes General purpose, deterministic
ob:aags AES-GCM-SIV Yes Yes Deterministic alternative
ob:apsv AES-SIV No Yes Maximum privacy protection
ob:apgs AES-GCM-SIV No Yes Probabilistic alternative
ob:upbc AES-CBC No No Unauthenticated - use with caution

Key Concepts:

Choosing a Scheme

Note on encryption strength: All a-tier and u-tier schemes MUST use 256-bit AES encryption. The z-tier uses 128-bit AES for performance in non-security contexts.


2. Algorithm

Oboron combines encryption and encoding in a single operation, requiring specific terminology:

The cryptographic ciphertext (bytes, not string) is an internal implementation detail, NOT exposed in the public API.

The high-level process flow is:

enc operation:
    [plaintext] (string)
      -> encryption -> [ciphertext] (bytes)
      -> encoding -> [obtext] (string)

dec operation:
    [obtext] (string)
      -> decoding -> [ciphertext] (bytes)
      -> decryption -> [plaintext] (string)

The above diagram is conceptual; actual implementation includes scheme-specific steps like scheme byte appending and (for z-tier schemes only) optional ciphertext prefix restructuring. With this middle-step included, the diagram becomes:

enc operation:
    [plaintext]
      -> encryption -> [ciphertext]
      -> oboron pack -> [payload]
      -> encoding -> [obtext]

dec operation:
    [obtext]
      -> decoding -> [payload]
      -> oboron unpack -> [ciphertext]
      -> decryption -> [plaintext]

In a-tier and u-tier schemes, the difference between the payload and the ciphertext is in the 2-byte scheme marker that is appended to the ciphertext, enabling scheme autodetection in decoding.

2.1 Payload Construction

This section formally specifies the "oboron pack" and "oboron unpack" steps that transform a raw ciphertext into the encoded payload and back. Implementations MUST conform to this algorithm for cross-implementation interoperability.

2.1.1 Payload Structure

The payload is the byte sequence produced by the oboron pack step, which is then encoded to produce the obtext. The payload is defined as:

payload = ciphertext || xored_marker[0] || xored_marker[1]

Where:

2.1.2 Scheme Bytes

Each scheme is identified by a 2-byte marker. The marker is a structured 16-bit value:

Byte 1 (bits 7..0): [ext:1][version:4][tier:3]

Byte 2 (bits 7..0): [properties:4][algorithm:4]

Concrete marker values for each scheme:

Scheme Tier Properties Algorithm Byte 1 Byte 2 Marker
aags 001 0001 0010 0x01 0x12 [0x01, 0x12]
apgs 001 0000 0010 0x01 0x02 [0x01, 0x02]
aasv 001 0001 0011 0x01 0x13 [0x01, 0x13]
apsv 001 0000 0011 0x01 0x03 [0x01, 0x03]
upbc 010 0000 0001 0x02 0x01 [0x02, 0x01]
zrbcx 110 0010 0001 0x06 0x21 [0x06, 0x21]

2.1.3 XOR Entropy Mixing

Before appending, each marker byte is XORed with the first byte of the ciphertext. This ensures the marker appears random in the encoded output, even for very short payloads.

Pack (enc direction):

marker    = scheme.marker()            // 2-byte raw marker
first_byte = ciphertext[0]
payload   = ciphertext || (marker[0] XOR first_byte) || (marker[1] XOR first_byte)

Unpack (dec direction):

first_byte = payload[0]
marker     = [ payload[n-2] XOR first_byte, payload[n-1] XOR first_byte ]
ciphertext = payload[0..n-2]

Where n is the length of the payload. Implementations MUST validate the recovered marker against the expected scheme marker and MUST return an error on mismatch.

2.1.4 Z-tier Prefix Restructuring

For z-tier schemes with prefix restructuring (e.g., zrbcx), the encryption function applies an additional XOR transformation before the scheme marker is appended:

if len(ciphertext) > AES_BLOCK_SIZE:
    ciphertext[0..16] = ciphertext[0..16] XOR ciphertext[last_16_bytes]

This concentrates entropy in the output prefix. This step is reversed during decryption before the standard CBC decryption.

2.2 Padding Design

Oboron's CBC schemes use a custom padding scheme optimized for UTF-8 strings:

Rationale: Oboron is defined to operate exclusively on UTF-8 strings, not arbitrary binary data. This is a protocol-level requirement: all enc operations MUST accept a UTF-8 string input and all dec operations MUST return a UTF-8 string. The 0x01 padding byte can never appear in valid UTF-8 input, ensuring unambiguous decoding. Under the UTF-8 input constraint, this padding is functionally equivalent to PKCS#7 and does not weaken security. Implementations MUST enforce the UTF-8 constraint, eliminating padding ambiguity errors at runtime.

2.3 Per-Scheme Ciphertext Layout

This section specifies the exact byte layout of the raw ciphertext produced by each scheme's encryption function (before the oboron pack step). Implementations MUST conform to these layouts for cross-implementation interoperability.

Deterministic schemes (fixed nonce, nonce not included in output)

Probabilistic schemes (random nonce prepended to output)

Summary table:

Scheme Algorithm Nonce/IV in output Ciphertext layout (bytes)
aasv AES-SIV No 16-byte SIV tag || encrypted data
aags AES-GCM-SIV No (zero nonce fixed) encrypted data || 16-byte auth tag
apsv AES-SIV 16-byte nonce (prepended) 16-byte nonce || 16-byte SIV tag || encrypted data
apgs AES-GCM-SIV 12-byte nonce (prepended) 12-byte nonce || encrypted data || 16-byte auth tag
upbc AES-CBC 16-byte IV (prepended) 16-byte IV || padded encrypted data

3. Key Management

3.1 Single Master Key Model

Oboron uses a single 512-bit master key partitioned into algorithm-specific subkeys using fixed byte offsets (no KDF):

Scheme Key bytes used Offset Length
ob:aasv, ob:apsv Full key (bytes 0–63) 0 64 bytes
ob:aags, ob:apgs Bytes 32–63 32 32 bytes
ob:upbc Bytes 8–39 8 32 bytes

AES-SIV key structure note: For ob:aasv and ob:apsv, the full 64-byte master key is passed directly to the AES-SIV primitive as the combined key per RFC 5297. AES-SIV internally splits this into two 256-bit subkeys: the first 32 bytes (bytes 0–31) serve as the S2V (CMAC) authentication key, and the second 32 bytes (bytes 32–63) serve as the CTR-mode encryption key. Implementations MUST pass all 64 bytes directly to the AES-SIV primitive and MUST NOT split them manually.

Design Rationale: This approach prioritizes low latency for short-string encryption. No hash-based KDF (e.g., HKDF) is used, as this would dominate runtime for intended workloads.

The master key MUST NOT leave the application. Algorithm-specific keys MUST be extracted on-the-fly and MUST NOT be cached or stored.

3.2 Key Format

The default key input format is base64. This is consistent with Oboron's strings-first API design. As any production use will typically read the key from an environment variable, this allows the string format to be directly fed into the constructor.

The base64 format was chosen for its compactness, as an 86-character base64 key is easier to handle manually (in secrets or environment variables management UI) than a 128-character hex key.

While any 512-bit key is accepted by Oboron, the keys generated by Oboron's key generation utilities MUST NOT include any dashes or underscores, in order to ensure the keys are double-click selectable, and to avoid any human visual parsing confusion due to underscores.

3.3 Valid Base64 Keys

Important technical detail: Not every 86-character base64 string is a valid 512-bit key. Since 512 bits requires 85.3 bytes when base64-encoded, the final character is constrained by padding requirements. When generating keys, implementations MUST use one of the following methods:

  1. use Oboron's key generation utility
  2. generate random 64 bytes, then encode as base64
  3. generate random 128 hex characters, then convert hexadecimal to base64

3.4 Alternative Key Interfaces

Implementations SHOULD support the following key input formats in addition to the default base64 format:


4. Protocol API

All Oboron implementations MUST provide the following abstract interface.

4.1 Core Operations

Empty plaintext: Implementations MUST reject empty string input. The enc operation MUST return an error when given an empty plaintext string.

4.2 Codec Construction

A codec MUST be constructible from a key and a format specifier. Implementations MUST support construction from a base64 key string (primary interface), as well as from a hex key string or raw key bytes.

Rust:

use oboron::AasvC32;

let ob = AasvC32::new(
    &env::var("OBORON_KEY")?
)?;

Python:

from oboron import AasvC32
import os

ob = AasvC32(os.getenv("OBORON_KEY"))

4.3 Key Generation

All implementations MUST provide a key generation utility that produces a valid 512-bit key encoded as a base64 string (86 characters, URL-safe alphabet, no padding characters).

Rust:

let key = oboron::generate_key();
cargo run --bin keygen

Python:

key = oboron.generate_key()
python -m oboron.keygen

5. Compatibility

Oboron implementations MUST maintain full cross-language compatibility:

All implementations MUST pass the common test vectors.