diff --git a/src/api/endpoints.rs b/src/api/endpoints.rs index f8cadf5..31895c2 100644 --- a/src/api/endpoints.rs +++ b/src/api/endpoints.rs @@ -100,3 +100,28 @@ pub const GET_STH: Endpoint = (reqwest::Method::GET, "/ct/v1/get-sth"); /// ``` pub const GET_STH_CONSISTENCY: Endpoint = (reqwest::Method::GET, "/ct/v1/get-sth-consistency"); + +/// Reference: https://datatracker.ietf.org/doc/html/rfc6962#section-4.4 +/// ```txt +/// GET https:///ct/v1/get-proof-by-hash +/// +/// Inputs: +/// +/// hash: A base64-encoded v1 leaf hash. +/// +/// tree_size: The tree_size of the tree on which to base the proof, +/// in decimal. +/// +/// The "hash" must be calculated as defined in Section 3.4. The +/// "tree_size" must designate an existing v1 STH. +/// +/// Outputs: +/// +/// leaf_index: The 0-based index of the end entity corresponding to +/// the "hash" parameter. +/// +/// audit_path: An array of base64-encoded Merkle Tree nodes proving +/// the inclusion of the chosen certificate. +/// ``` +pub const GET_PROOF_BY_HASH: Endpoint = + (reqwest::Method::GET, "/ct/v1/get-proof-by-hash"); diff --git a/src/api/mod.rs b/src/api/mod.rs index 2ea8e44..8f158a3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -2,6 +2,7 @@ use reqwest::Url; use responses::{ AddChainRequest, AddChainResponse, + GetProofByHashResponse, GetSthConsistencyResponse, GetSthResponse }; @@ -133,4 +134,36 @@ impl CtApiClient { .json() .await } + + /// Fetches a single merkle audit proof for a specific leaf node by hash + /// from the CT log. The response both includes the index of the hashed + /// leaf node and the list of Merkle Tree nodes required to verify proof of + /// existence. + /// + /// See: [`endpoints::GET_PROOF_BY_HASH`] + /// + /// ## Errors + /// + /// This may error if either the request failed (due to lack of internet or + /// invalid domain, for example), or if the CT log gave a 4xx/5xx response. + /// The CT log may error the response if your provided hash was not a hash + /// of a valid node at the tree size specified. + pub async fn get_merkle_audit_proof_by_hash( + &self, + hash: &str, + tree_size: u64 + ) -> reqwest::Result { + self.inner_client + .request( + endpoints::GET_PROOF_BY_HASH.0, + self.log_url.to_string() + endpoints::GET_PROOF_BY_HASH.1 + ) + .query(&[("hash", hash)]) + .query(&[("tree_size", tree_size)]) + .send() + .await? + .error_for_status()? + .json() + .await + } } diff --git a/src/api/responses.rs b/src/api/responses.rs index de78f28..f8d7429 100644 --- a/src/api/responses.rs +++ b/src/api/responses.rs @@ -41,3 +41,13 @@ pub struct GetSthResponse { pub struct GetSthConsistencyResponse { pub consistency: Vec } + +/// A response given when fetching the Merkle Audit Proof from a CT log merkle +/// leaf. +/// +/// See: [`super::endpoints::GET_PROOF_BY_HASH`] +#[derive(Debug, Deserialize)] +pub struct GetProofByHashResponse { + pub leaf_index: u64, + pub audit_path: Vec +}