odoh-rs

[odoh-rs] is a library that implements [Oblivious DNS over HTTPS (ODoH) protocol draft-02] in Rust.

It can be used to implement an ODoH client or server (target). [odoh-client-rs] uses odoh-rs to implement its functionality, and is a good source of API usage examples, along with the tests in odoh-rs, in particular [testvectorsfor_odoh].

This library is interoperable with [odoh-go].

odoh-rs uses [hpke] as the underlying HPKE implementation. It supports the default Oblivious DoH ciphersuite (KEM: X25519HkdfSha256, KDF: HkdfSha256, AEAD: AesGcm128).

It does not provide full crypto agility, but if you want to use another HPKE suite, you can fork the library and make the relevant changes in key_utils and protocol.rs marked CHANGE THIS.

Example API Usage

This example outlines the steps necessary for a successful ODoH query.

```rust // Server generates a secret key pair fn generatekeypair() -> ObliviousDoHKeyPair { // random bytes, should be 32 bytes for X25519 keys let ikm = rand::threadrng().gen::<[u8; 32]>();; let (secretkey, publickey) = Kem::derivekeypair(&ikm); let publickeybytes = publickey.tobytes().tovec(); let odohpublickey = ObliviousDoHConfigContents { kemid: 0x0020, // DHKEM(X25519, HKDF-SHA256) kdfid: 0x0001, // KDF(SHA-256) aeadid: 0x0001, // AEAD(AES-GCM-128) publickey: publickeybytes, }; ObliviousDoHKeyPair { privatekey: secretkey, publickey: odohpublickey, } }

[tokio::main]

async fn main() -> Result<()> { // Server generates a key pair and creates an ObliviousDoHConfigs struct from it // which it will distribute to clients via HTTPS records as outlined in the draft: // https://tools.ietf.org/html/draft-pauly-dprive-oblivious-doh-02#section-5 let keypair = generatekeypair(); let config = ObliviousDoHConfig::new(&keypair.publickey.clone().tobytes().unwrap()).unwrap(); let odohconfig = ObliviousDoHConfigs { configs: vec![config.clone()], } .to_bytes() .unwrap();

// Client gets `odohconfig` via an HTTPS record
let client_config = get_supported_config(&odohconfig).unwrap();

// Client creates a query body
let query = ObliviousDoHQueryBody::new(&vec![1, 2], Some(2));

// Client creates a query to send to the server
let (oblivious_query, client_secret) = create_query_msg(&client_config, &query).unwrap();

// Server receives the query and parses it
let (parsed_query, server_secret) = parse_received_query(&key_pair, &oblivious_query)
    .await
    .unwrap();

// Server generates a DNS response based on the query
let resolver_resp = vec![1, 3, 4];

// Server creates an encrypted response msg to send to the client
let generated_response = create_response_msg(&server_secret, &resolver_resp, None, &query)
    .await
    .unwrap();

// Client receives the server's encrypted DNS response and parses it to recover the plaintext DNS response.
let parsed_response =
    parse_received_response(&client_secret, &generated_response, &query).unwrap();
Ok(())

}

```