1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
/*!

[odoh-rs] is a library that implements [Oblivious DNS over HTTPS (ODoH) protocol draft-06] 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 [test_vectors_for_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 crypto agility.

[odoh-rs]: https://github.com/cloudflare/odoh-rs/
[Oblivious DNS over HTTPS (ODoH) protocol draft-06]: https://tools.ietf.org/html/draft-pauly-dprive-oblivious-doh-06
[odoh-client-rs]: https://github.com/cloudflare/odoh-client-rs/
[odoh-go]: https://github.com/cloudflare/odoh-go
[test_vectors_for_odoh]: https://github.com/cloudflare/odoh-rs/src/protocol.rs#L639
[hpke]: https://docs.rs/hpke/0.3.1/hpke/index.html
[protocol.rs]: https://github.com/cloudflare/odoh-rs/src/protocol.rs

# Example API usage

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

```
# use crate::odoh_rs::{
#    key_utils::derive_keypair_from_seed,
#    protocol::{
#       create_query_msg, create_response_msg, get_supported_config, parse_received_query,
#       parse_received_response, ObliviousDoHConfig, ObliviousDoHConfigContents, ObliviousDoHConfigs,
#       ObliviousDoHKeyPair, ObliviousDoHQueryBody, Serialize, ODOH_VERSION,
#    }
# };
# use anyhow::Result;
# use hex;
# use hpke::{kem::X25519HkdfSha256, kex::KeyExchange, Kem as KemTrait, Serializable};
# use rand::Rng;
# pub type Kem = X25519HkdfSha256;
# pub type Kex = <Kem as KemTrait>::Kex;
// Server generates a secret key pair
fn generate_key_pair() -> ObliviousDoHKeyPair {
    // random bytes, should be 32 bytes for X25519 keys
    let ikm = rand::thread_rng().gen::<[u8; 32]>();;
    let (secret_key, public_key) = derive_keypair_from_seed(&ikm);
    let public_key_bytes = public_key.to_bytes().to_vec();
    let odoh_public_key = ObliviousDoHConfigContents {
        kem_id: 0x0020,  // DHKEM(X25519, HKDF-SHA256)
        kdf_id: 0x0001,  // KDF(SHA-256)
        aead_id: 0x0001, // AEAD(AES-GCM-128)
        public_key: public_key_bytes,
    };
    ObliviousDoHKeyPair {
        private_key: secret_key,
        public_key: odoh_public_key,
    }
}

#[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 key_pair = generate_key_pair();
    let config = ObliviousDoHConfig::new(&key_pair.public_key.clone().to_bytes().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
    // with no padding and no prespecified response nonce
    let generated_response = create_response_msg(&server_secret, &resolver_resp, None, 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(())
}

```
*/

pub mod key_utils;
pub mod protocol;