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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
use hpke::{
kem::X25519HkdfSha256,
Kem as KemTrait, Serializable,
};
use odoh_rs::protocol::{
create_response_msg, parse_received_query,
ObliviousDoHConfigContents, ObliviousDoHKeyPair,
ObliviousDoHQueryBody,
};
use futures::executor;
use hex;
use wasm_bindgen::prelude::*;
pub type Kem = X25519HkdfSha256;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
pub const ODOH_VERSION: u16 = 0x0001;
const KEM_ID: u16 = 0x0020;
const KDF_ID: u16 = 0x0001;
const AEAD_ID: u16 = 0x0001;
// random bytes, should be 32 bytes for X25519 keys
pub const IKM: &str = "871389a8727130974e3eb3ee528d440a871389a8727130974e3eb3ee528d440a";
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
// The `console.log` is quite polymorphic, so we can bind it with multiple
// signatures. Note that we need to use `js_name` to ensure we always call
// `log` in JS.
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);
// Multiple arguments too!
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
}
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
fn generate_key_pair() -> ObliviousDoHKeyPair {
let ikm_bytes = hex::decode(IKM).unwrap();
let (secret_key, public_key) = Kem::derive_keypair(&ikm_bytes);
let public_key_bytes = public_key.to_bytes().to_vec();
let odoh_public_key = ObliviousDoHConfigContents {
kem_id: KEM_ID,
kdf_id: KDF_ID,
aead_id: AEAD_ID,
public_key: public_key_bytes,
};
ObliviousDoHKeyPair {
private_key: secret_key,
public_key: odoh_public_key,
}
}
#[wasm_bindgen]
pub fn get_odoh_config() -> js_sys::Uint8Array {
let key_pair = generate_key_pair();
let public_key_bytes = key_pair.public_key.public_key;
let length_bytes = (public_key_bytes.len() as u16).to_be_bytes();
let odoh_config_length = 12 + public_key_bytes.len();
let version = ODOH_VERSION;
let odoh_contents_length = 8 + public_key_bytes.len();
let kem_id = KEM_ID; // DHKEM(X25519, HKDF-SHA256)
let kdf_id = KDF_ID; // KDF(SHA-256)
let aead_id = AEAD_ID; // AEAD(AES-GCM-128)
let mut result = vec![];
result.extend(&((odoh_config_length as u16).to_be_bytes()));
result.extend(&((version as u16).to_be_bytes()));
result.extend(&((odoh_contents_length as u16).to_be_bytes()));
result.extend(&((kem_id as u16).to_be_bytes()));
result.extend(&((kdf_id as u16).to_be_bytes()));
result.extend(&((aead_id as u16).to_be_bytes()));
result.extend(&length_bytes);
result.extend(&public_key_bytes);
return js_sys::Uint8Array::from(&result[..]);
}
static mut QUERY_BODY: Option<ObliviousDoHQueryBody> = None;
static mut SERVER_SECRET: Option<Vec<u8>> = None;
#[wasm_bindgen]
pub fn decrypt_query(
odoh_encrypted_query_msg: &[u8],
) -> js_sys::Uint8Array {
let mut result = vec![];
unsafe {
let key_pair = generate_key_pair();
let parsed_res =
executor::block_on(parse_received_query(&key_pair, &odoh_encrypted_query_msg));
let (parsed_query, secret) = match parsed_res {
Ok(t) => (t.0, t.1),
Err(_) => {
console_log!("parse_received_query failed!");
return js_sys::Uint8Array::new_with_length(0)
},
};
result.extend(&parsed_query.dns_msg);
QUERY_BODY = Some(parsed_query);
SERVER_SECRET = Some(secret);
}
return js_sys::Uint8Array::from(&result[..]);
}
#[wasm_bindgen]
pub fn create_response(
response: &[u8],
) -> js_sys::Uint8Array {
unsafe {
if let Some(body) = &QUERY_BODY {
if let Some(secret) = &SERVER_SECRET {
// random bytes
let nonce = vec![0x1b, 0xff, 0xfd, 0xff, 0x1a, 0xff, 0xff, 0xff,
0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xe];
let result = executor::block_on(create_response_msg(
&secret,
&response,
None,
Some(nonce),
&body,
));
let generated_response = match result {
Ok(r) => r,
Err(_) => {
console_log!("create_response_msg failed!");
return js_sys::Uint8Array::new_with_length(0);
}
};
QUERY_BODY = None;
SERVER_SECRET = None;
return js_sys::Uint8Array::from(&generated_response[..]);
}
}
}
console_log!("create_response_msg failed!");
return js_sys::Uint8Array::new_with_length(0);
}
|