JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 3393
  • Score
    100M100P100Q103797F
  • License MIT

Oblivious HTTP (RFC 9458) implementation with chunked extension support

Package Exports

  • ohttp-ts

Readme

ohttp-ts

NPM License

TypeScript implementation of Oblivious HTTP (RFC 9458) with streaming support.

Features

  • RFC 9458 - Oblivious HTTP
  • Chunked OHTTP - Streaming extension (draft-ietf-ohai-chunked-ohttp-08)
  • WebCrypto - Works in browsers, Cloudflare Workers, Node.js 22+

Installation

npm install ohttp-ts hpke

Quick Start

import { CipherSuite, KEM_DHKEM_X25519_HKDF_SHA256, KDF_HKDF_SHA256, AEAD_AES_128_GCM } from "hpke";
import { KeyConfig, OHTTPClient, OHTTPServer, KdfId, AeadId } from "ohttp-ts";

// Gateway: generate key configuration
const suite = new CipherSuite(KEM_DHKEM_X25519_HKDF_SHA256, KDF_HKDF_SHA256, AEAD_AES_128_GCM);
const keyConfig = await KeyConfig.generate(suite, 0x01, [
  { kdfId: KdfId.HKDF_SHA256, aeadId: AeadId.AES_128_GCM },
]);
const gateway = new OHTTPServer([keyConfig]);

// Client: fetch and parse gateway's public key
const publicKeyBytes = KeyConfig.serialize(keyConfig);
const clientKeyConfig = KeyConfig.parse(publicKeyBytes);
const client = new OHTTPClient(suite, clientKeyConfig);

// Client: encapsulate HTTP request
const httpRequest = new Request("https://target.example/api", {
  method: "POST",
  body: JSON.stringify({ data: "sensitive" }),
});
const { request: relayRequest, context } = await client.encapsulateRequest(
  httpRequest,
  "https://relay.example/ohttp",
);

// Send to relay: fetch(relayRequest)

// Gateway: decapsulate request
const { request: innerRequest, context: serverContext } = await gateway.decapsulateRequest(relayRequest);
// innerRequest is the original Request object

// Gateway: encapsulate response
const httpResponse = new Response(JSON.stringify({ result: "ok" }), { status: 200 });
const encapsulatedResponse = await serverContext.encapsulateResponse(httpResponse);

// Client: decapsulate response
const innerResponse = await context.decapsulateResponse(encapsulatedResponse);
// innerResponse is the original Response object

Protocol Flow

+---------+       +-------+       +---------+    +--------+
| Client  |       | Relay |       | Gateway |    | Target |
+---------+       +-------+       +---------+    +--------+
     |                |                |             |
     | Encapsulated   |                |             |
     | Request        |                |             |
     +--------------->| Forward        |             |
     |                +--------------->| Decrypt &   |
     |                |                | Forward     |
     |                |                +------------>|
     |                |                |             |
     |                |                |<------------+
     |                |                | Encrypt     |
     |                |<---------------+ Response    |
     |<---------------+                |             |
     | Decapsulated   |                |             |
     | Response       |                |             |

Binary HTTP

OHTTP encapsulates Binary HTTP (RFC 9292) messages. The high-level API (encapsulateRequest, decapsulateRequest, etc.) handles encoding automatically.

For advanced use cases, the low-level bytes API is also available:

// Low-level API: work with raw Binary HTTP bytes
const { encapsulatedRequest, context } = await client.encapsulate(binaryHttpBytes);
const { request: binaryBytes, context: serverCtx } = await gateway.decapsulate(encapsulatedRequest);

See examples/bhttp.example.ts for a complete example.

Chunked OHTTP

For streaming large responses, use ChunkedOHTTPClient/ChunkedOHTTPServer with the low-level bytes API. The high-level Request/Response API requires buffering the entire body for Binary HTTP encoding, so chunked mode operates on raw bytes only.

Examples

Example Description
ohttp.example.ts Basic OHTTP round-trip
chunked.example.ts Streaming with chunked OHTTP (low-level bytes API)
bhttp.example.ts Request/Response API
mlkem.example.ts Post-quantum with ML-KEM-768

Post-Quantum Support

For post-quantum key encapsulation (ML-KEM), use @panva/hpke-noble:

npm install @panva/hpke-noble
import { CipherSuite } from "hpke";
import { KEM_ML_KEM_768, KDF_HKDF_SHA256, AEAD_AES_128_GCM } from "@panva/hpke-noble";

const suite = new CipherSuite(KEM_ML_KEM_768, KDF_HKDF_SHA256, AEAD_AES_128_GCM);
// Use with KeyConfig.generate(), OHTTPClient, OHTTPServer as usual

Security Considerations

Not audited. Use at your own risk.

  • Replay protection is out of scope (RFC 9458 Section 6.5)
  • Decryption errors are opaque to prevent oracle attacks

License

MIT