Avoid overriding $RUSTFLAGS when needed for rustc 1.63
[dnssec-prover] / wasmpack / src / lib.rs
1 //! WASM-compatible verification wrappers
2
3 use dnssec_prover::ser::parse_rr_stream;
4 use dnssec_prover::validation::{verify_rr_stream, ValidationError};
5 use dnssec_prover::rr::Name;
6 use dnssec_prover::query::{ProofBuilder, QueryBuf};
7
8 use wasm_bindgen::prelude::wasm_bindgen;
9
10 extern crate alloc;
11 use alloc::collections::VecDeque;
12
13 use core::fmt::Write;
14
15 #[global_allocator]
16 static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
17
18 #[wasm_bindgen]
19 pub struct WASMProofBuilder(ProofBuilder, VecDeque<QueryBuf>);
20
21 #[wasm_bindgen]
22 /// Builds a proof builder which can generate a proof for records of the given `ty`pe at the given
23 /// `name`.
24 ///
25 /// After calling this [`get_next_query`] should be called to fetch the initial query.
26 pub fn init_proof_builder(mut name: String, ty: u16) -> Option<WASMProofBuilder> {
27         if !name.ends_with('.') { name.push('.'); }
28         if let Ok(qname) = name.try_into() {
29                 let (builder, initial_query) = ProofBuilder::new(&qname, ty);
30                 let mut queries = VecDeque::with_capacity(4);
31                 queries.push_back(initial_query);
32                 Some(WASMProofBuilder(builder, queries))
33         } else {
34                 None
35         }
36 }
37
38 #[wasm_bindgen]
39 /// Processes a response to a query previously fetched from [`get_next_query`].
40 ///
41 /// After calling this, [`get_next_query`] should be called until pending queries are exhausted and
42 /// no more pending queries exist, at which point [`get_unverified_proof`] should be called.
43 pub fn process_query_response(proof_builder: &mut WASMProofBuilder, response: Vec<u8>) {
44         if response.len() < u16::MAX as usize {
45                 let mut answer = QueryBuf::new_zeroed(response.len() as u16);
46                 answer.copy_from_slice(&response);
47                 if let Ok(queries) = proof_builder.0.process_response(&answer) {
48                         for query in queries {
49                                 proof_builder.1.push_back(query);
50                         }
51                 }
52         }
53 }
54
55
56 #[wasm_bindgen]
57 /// Gets the next query (if any) that should be sent to the resolver for the given proof builder.
58 ///
59 /// Once the resolver responds [`process_query_response`] should be called with the response.
60 pub fn get_next_query(proof_builder: &mut WASMProofBuilder) -> Option<Vec<u8>> {
61         if let Some(query) = proof_builder.1.pop_front() {
62                 Some(query.into_vec())
63         } else {
64                 None
65         }
66 }
67
68 #[wasm_bindgen]
69 /// Gets the final, unverified, proof once all queries fetched via [`get_next_query`] have
70 /// completed and their responses passed to [`process_query_response`].
71 pub fn get_unverified_proof(proof_builder: WASMProofBuilder) -> Option<Vec<u8>> {
72         proof_builder.0.finish_proof().ok().map(|(proof, _ttl)| proof)
73 }
74
75 #[wasm_bindgen]
76 /// Verifies an RFC 9102-formatted proof and returns verified records matching the given name
77 /// (resolving any C/DNAMEs as required).
78 pub fn verify_byte_stream(stream: Vec<u8>, name_to_resolve: String) -> String {
79         let name = match Name::try_from(name_to_resolve) {
80                 Ok(name) => name,
81                 Err(()) => return "{\"error\":\"Bad name to resolve\"}".to_string(),
82         };
83         match do_verify_byte_stream(stream, name) {
84                 Ok(r) => r,
85                 Err(e) => format!("{{\"error\":\"{:?}\"}}", e),
86         }
87 }
88
89 fn do_verify_byte_stream(stream: Vec<u8>, name_to_resolve: Name) -> Result<String, ValidationError> {
90         let rrs = parse_rr_stream(&stream).map_err(|()| ValidationError::Invalid)?;
91         let verified_rrs = verify_rr_stream(&rrs)?;
92         let resolved_rrs = verified_rrs.resolve_name(&name_to_resolve);
93         let mut resp = String::new();
94         write!(&mut resp, "{}",
95                 format_args!("{{\"valid_from\": {}, \"expires\": {}, \"max_cache_ttl\": {}, \"verified_rrs\": [",
96                 verified_rrs.valid_from, verified_rrs.expires, verified_rrs.max_cache_ttl)
97         ).expect("Write to a String shouldn't fail");
98         for (idx, rr) in resolved_rrs.iter().enumerate() {
99                 write!(&mut resp, "{}{}", if idx != 0 { ", " } else { "" }, rr.json())
100                         .expect("Write to a String shouldn't fail");
101         }
102         resp += "]}";
103         Ok(resp)
104 }