mirror of
https://github.com/openai/codex.git
synced 2026-11-12 21:20:34 +00:00
docs(exec-server): explain Noise channel flow
Co-authored-by: Codex noreply@openai.com
This commit is contained in:
co-authored by
Codex noreply@openai.com
parent
3088d8f27e
commit
b227892a48
@@ -1,3 +1,10 @@
|
||||
//! Bridges AWS-LC's ML-KEM-768 implementation into Clatter's `Kem` trait.
|
||||
//!
|
||||
//! Clatter owns the hybrid Noise state machine, while AWS-LC owns the concrete
|
||||
//! post-quantum operations and their randomness. This module is intentionally
|
||||
//! limited to converting between the two libraries' fixed-size key, ciphertext,
|
||||
//! and shared-secret representations; it does not add protocol behavior.
|
||||
|
||||
use aws_lc_rs::kem::Ciphertext;
|
||||
use aws_lc_rs::kem::DecapsulationKey;
|
||||
use aws_lc_rs::kem::EncapsulationKey;
|
||||
@@ -33,6 +40,10 @@ impl Kem for AwsLcMlKem768 {
|
||||
type Ct = HeapArray<CIPHERTEXT_LEN>;
|
||||
type Ss = SensitiveByteArray<[u8; SHARED_SECRET_LEN]>;
|
||||
|
||||
// Generate the long-lived static KEM keypair used by hybrid IK. AWS-LC
|
||||
// returns serialized key bytes; copying them into Clatter's fixed-size
|
||||
// containers gives the handshake implementation the exact lengths encoded
|
||||
// by this suite and keeps the secret half in a zeroizing container.
|
||||
fn genkey_rng<R: Rng>(_rng: &mut R) -> KemResult<KeyPair<Self::PubKey, Self::SecretKey>> {
|
||||
// AWS-LC owns ML-KEM key-generation randomness internally, so
|
||||
// Clatter's injectable RNG cannot be plumbed through this provider.
|
||||
@@ -54,6 +65,9 @@ impl Kem for AwsLcMlKem768 {
|
||||
})
|
||||
}
|
||||
|
||||
// Parse the peer's serialized public key at the provider boundary, then
|
||||
// return the ciphertext and shared secret in Clatter's fixed-size types.
|
||||
// AWS-LC performs both public-key validation and encapsulation randomness.
|
||||
fn encapsulate<R: Rng>(pk: &[u8], _rng: &mut R) -> KemResult<(Self::Ct, Self::Ss)> {
|
||||
let encapsulation_key =
|
||||
EncapsulationKey::new(&ML_KEM_768, pk).map_err(|_| KemError::Input)?;
|
||||
@@ -67,6 +81,8 @@ impl Kem for AwsLcMlKem768 {
|
||||
))
|
||||
}
|
||||
|
||||
// Reconstruct the provider key from process-local secret bytes and recover
|
||||
// the same shared secret the peer mixed into the hybrid handshake.
|
||||
fn decapsulate(ct: &[u8], sk: &[u8]) -> KemResult<Self::Ss> {
|
||||
// Reject the length before constructing AWS-LC's ciphertext wrapper.
|
||||
// This keeps malformed wire input classified as an input error rather
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
//! Narrow, misuse-resistant wrapper around the Clatter primitives used by the
|
||||
//! remote exec-server relay.
|
||||
//!
|
||||
//! The harness is the Noise hybrid-IK initiator and pins the registry-provided
|
||||
//! exec-server static public key. The exec-server is the responder; after it
|
||||
//! reads the first IK message, Noise exposes the authenticated harness static
|
||||
//! public key for registry authorization. Application data is allowed only
|
||||
//! after both the cryptographic handshake and that external authorization pass.
|
||||
//! # Protocol overview
|
||||
//!
|
||||
//! Noise is a framework for turning Diffie-Hellman operations into an
|
||||
//! authenticated handshake and then an encrypted byte channel. This module uses
|
||||
//! the `IK` handshake pattern: the harness is the initiator and already knows
|
||||
//! the exec-server's static public key, while the exec-server learns and
|
||||
//! authenticates the harness's static public key from the first handshake
|
||||
//! message. That lets the harness reject the wrong executor immediately and
|
||||
//! gives the executor a cryptographic identity it can authorize with the
|
||||
//! environment registry.
|
||||
//!
|
||||
//! The suite is "hybrid" because the handshake combines classical X25519 with
|
||||
//! post-quantum ML-KEM-768. Clatter runs the Noise state machine and mixes both
|
||||
//! key-agreement results into the session keys; AWS-LC supplies the ML-KEM
|
||||
//! operations. AES-GCM then protects ordered transport records after the two
|
||||
//! handshake messages complete.
|
||||
//!
|
||||
//! The handshake authenticates keys, not product permissions. The first message
|
||||
//! therefore carries a registry-issued harness authorization inside its
|
||||
//! encrypted payload. The exec-server pauses after authenticating that message,
|
||||
//! asks the registry whether the authenticated harness key is allowed, and only
|
||||
//! then sends the second handshake message and exposes JSON-RPC. Application
|
||||
//! data is never accepted before both checks pass.
|
||||
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
@@ -18,6 +36,11 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::aws_lc_ml_kem::AwsLcMlKem768;
|
||||
|
||||
/// Stable identifier for the complete handshake and transport algorithm suite.
|
||||
///
|
||||
/// This value travels with public keys so configuration cannot silently combine
|
||||
/// key material generated for a different Noise pattern or algorithm set.
|
||||
pub const NOISE_CHANNEL_SUITE: &str = "Noise_hybridIK_X25519+MLKEM768_AESGCM_SHA256";
|
||||
|
||||
type DhKeyPair = KeyPair<<X25519 as Dh>::PubKey, <X25519 as Dh>::PrivateKey>;
|
||||
@@ -38,6 +61,9 @@ pub struct NoiseChannelPublicKey {
|
||||
|
||||
impl std::fmt::Debug for NoiseChannelPublicKey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Public keys are not secrets, but logging complete identities makes
|
||||
// correlation across environments unnecessarily easy. Keep only the
|
||||
// suite visible in routine diagnostics.
|
||||
f.debug_struct("NoiseChannelPublicKey")
|
||||
.field("suite", &self.suite)
|
||||
.field("x25519_public_key", &"<redacted>")
|
||||
@@ -47,6 +73,10 @@ impl std::fmt::Debug for NoiseChannelPublicKey {
|
||||
}
|
||||
|
||||
impl NoiseChannelPublicKey {
|
||||
/// Serialize both public components as one suite-tagged registry value.
|
||||
///
|
||||
/// Keeping the components together prevents callers from accidentally
|
||||
/// pairing an X25519 key from one identity with an ML-KEM key from another.
|
||||
fn from_keypairs(dh: &DhKeyPair, kem: &KemKeyPair) -> Self {
|
||||
Self {
|
||||
suite: NOISE_CHANNEL_SUITE.to_string(),
|
||||
@@ -68,6 +98,9 @@ pub struct NoiseChannelIdentity {
|
||||
|
||||
impl std::fmt::Debug for NoiseChannelIdentity {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// Never delegate to the keypair debug implementations: this type owns
|
||||
// both private keys. Its public projection is sufficient to identify
|
||||
// which endpoint identity a log entry refers to.
|
||||
f.debug_struct("NoiseChannelIdentity")
|
||||
.field("public_key", &self.public_key())
|
||||
.finish_non_exhaustive()
|
||||
|
||||
Reference in New Issue
Block a user