[odoh-rs] is a library that implements [Oblivious DNS over HTTPS (ODoH) protocol draft-04] 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.
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) = derivekeypairfromseed(&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, } }
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(())
}
```