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