Swift Digest
Blog | Swift.org Blog

Swift での Oblivious HTTP サポートの登場

Introducing Oblivious HTTP support in Swift

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

この記事の要点

何が発表されたのか

SwiftNIO Oblivious HTTP は、Oblivious HTTP を Swift エコシステムで利用できるようにするパッケージで、高性能なネットワークコードを開発するための SwiftNIO プロジェクトの一部です。

Oblivious HTTP は、サーバーがリクエストの送信元を識別できないようにしたまま、クライアントがサーバーへリクエストを送れるようにするプロトコルです。通常の HTTP リクエストは、送信元 IP アドレスのようなクライアントを識別し得る情報を露出させたり、同一クライアントからの複数リクエストが同じノードに由来すると判別できてしまったりします。Oblivious HTTP は、HTTP メッセージの暗号化と信頼できる第三者のリレーサービスを組み合わせることで、この識別情報を保護する安全な仕組みを提供します。性能上の大きなオーバーヘッドを伴わずにユーザーのプライバシーを高められる点が特徴です。

Oblivious HTTP は、プライバシーを保護するネットワーク技術の基盤となる重要な技術で、DNS のプライバシー強化の提案などにも使われています。Apple はこれを Private Cloud Compute でも利用しており、リクエストを処理する側のデバイスに送信元の個人を特定し得るデータが渡らないようにし、個人のデータリクエストを狙った攻撃に対してシステムを強化しています。

このパッケージは 2 つのライブラリに分かれています。

  1. ObliviousHTTP: Oblivious HTTP を実装するのに必要な、RFC 9292 のバイナリ HTTP エンコーディング方式の実装と、その方式の SwiftNIO 向けバインディングを提供します。
  2. ObliviousX: Oblivious HTTP が使う暗号化方式の基礎となる、より汎用的な API を提供します。バイナリ HTTP メッセージだけでなく、任意のエンコーディングにも適用できます。

これら 2 つは一緒にも別々にも使えます。

何に使えるのか

バイナリ HTTP エンコーディング

RFC 9292 のバイナリ HTTP 表現は、HTTP/1.1 や HTTP/2 といった特定のワイヤフォーマットに依存しない、抽象的な HTTP メッセージをシリアライズ・デシリアライズする仕組みを定義します。ObliviousHTTP は、これに対するシンプルなシリアライズ・デシリアライズ API を提供します。

import ObliviousHTTP
import NIOCore
import NIOHTTP1

let serializer = BHTTPSerializer()
var buffer = ByteBuffer()
serializer.serialize(.request(.head(.init(method: .GET))), into: &buffer)
serializer.serialize(.request(.body(payload)), into: &buffer)
serializer.serialize(.request(.end(nil)), into: &buffer)

var parser = BHTTPParser(role: .server)
parser.append(buffer)
parser.completeBodyReceived()

while let message = try parser.nextMessage() {
    print(message)
}

Oblivious なカプセル化

Oblivious HTTP の仕様は、HTTP のバイナリシリアライズ形式と、Hybrid Public Key Encryption (HPKE) の上に構築された暗号化方式を組み合わせたものです。この暗号化方式は完全に汎用的なので、バイナリ HTTP メッセージに限らず任意のデータに使えます。

ObliviousX ライブラリは、このカプセル化方式を扱う一連の API を提供します。API には 2 種類あり、RFC 9458 の OHTTP 方式を実装する single-shot 関数と、chunked OHTTP のドラフト仕様で定義された方式を実装するストリーミング版があります。次は single-shot API の使用例です。

import ObliviousX

// Encryption

let message = Data("Hello, this is my secret message".utf8)
let (encapsulatedRequest, sender) = OHTTPEncapsulation.encapsulateRequest(
    keyID: serverKeyID,
    publicKey: serverPublicKey,
    ciphersuite: .P256_SHA256_AES_GCM_256,
    mediaType: "text/plain",
    content: message
)

// Decryption
let (header, consumedBytes) = OHTTPEncapsulation.parseRequestHeader(
    encapsulatedRequest: encapsulatedRequest
)
assert(header.keyID == serverKeyID)
assert(header.kem == .P256_HKDF_SHA256)
assert(header.kdf == .HKDF_SHA256)
assert(header.aead == .AES_GCM_256)

let decapsulator = OHTTPEncapsulation.RequestDecapsulator(
    requestHeader: header,
    message: encapsulatedRequest.dropFirst(consumedBytes)
)
let (deEncapsulated, context) = try decapsulator.decapsulate(
    mediaType: "text/plain",
    privateKey: serverPrivateKey
)

assert(deEncapsulated == message)

導入・今後の位置づけ

このパッケージは、フィードバックと貢献を募るために早期開発段階で公開されています。GitHub のリポジトリで試したり、Swift フォーラムで議論や改善提案に参加したりできます。

中核となる暗号化方式は十分に機能すると考えられている一方で、バイナリ HTTP と Oblivious HTTP のバインディング一式については設計作業が継続中です。発表時点では、次の項目がロードマップとして挙げられています。いずれも将来の構想であり、実現を約束するものではありません。

  1. ChannelPipeline にそのまま組み込める ChannelHandler の形での、より良い SwiftNIO バインディングの提供。
  2. SwiftNIO の型ではなく swift-http-types を使うバージョンのバイナリ HTTP の提供。Swift エコシステムの他の部分との組み合わせやすさを高め、SwiftNIO への依存自体をなくせる可能性があります。
  3. IETF で chunked Oblivious HTTP のドラフトが確定した際の正式サポート。
  4. より幅広いユースケースに対応するためのその他の API 調整。

関連リンク