feat(api): Start on API requests with add-chain and boilerplate
This commit is contained in:
parent
84aa99f261
commit
032a99f4fc
7 changed files with 125 additions and 5 deletions
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -18,5 +18,7 @@
|
|||
"**/CVS": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true
|
||||
}
|
||||
},
|
||||
"rust-analyzer.cargo.features": "all",
|
||||
"rust-analyzer.check.features": "all"
|
||||
}
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -189,7 +189,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"tokio",
|
||||
"x509-parser",
|
||||
|
|
|
@ -8,9 +8,8 @@ anyhow = "1.0.90"
|
|||
der-parser = "9.0.0"
|
||||
nom = "7.1.3"
|
||||
num-traits = "0.2.19"
|
||||
reqwest = { version = "0.12.8", features = ["json"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
reqwest = { version = "0.12.8", features = ["json"], optional = true }
|
||||
serde = { version = "1.0.210", features = ["derive"], optional = true }
|
||||
sha2 = "0.10.8"
|
||||
x509-parser = "0.16.0"
|
||||
|
||||
|
@ -19,3 +18,7 @@ base64ct = "1.6.0"
|
|||
reqwest = { version = "0.12.8", features = ["json"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
tokio = { version = "1.41.0", features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
[features]
|
||||
default-features = []
|
||||
api = ["dep:reqwest", "dep:serde"]
|
||||
|
|
46
src/api/endpoints.rs
Normal file
46
src/api/endpoints.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
//! A module containing constants for each API endpoint, specifying the method
|
||||
//! and path of each. This is primarily used for the [api module](`crate::api`)
|
||||
//! internals, but could be used externally also.
|
||||
|
||||
type Endpoint<'a> = (reqwest::Method, &'a str);
|
||||
|
||||
/// Reference: https://datatracker.ietf.org/doc/html/rfc6962#section-4.1
|
||||
/// ```txt
|
||||
/// POST https://<log server>/ct/v1/add-chain
|
||||
///
|
||||
/// Inputs:
|
||||
///
|
||||
/// chain: An array of base64-encoded certificates. The first
|
||||
/// element is the end-entity certificate; the second chains to the
|
||||
/// first and so on to the last, which is either the root
|
||||
/// certificate or a certificate that chains to a known root
|
||||
/// certificate.
|
||||
///
|
||||
/// Outputs:
|
||||
///
|
||||
/// sct_version: The version of the SignedCertificateTimestamp
|
||||
/// structure, in decimal. A compliant v1 implementation MUST NOT
|
||||
/// expect this to be 0 (i.e., v1).
|
||||
///
|
||||
/// id: The log ID, base64 encoded. Since log clients who request an
|
||||
/// SCT for inclusion in TLS handshakes are not required to verify
|
||||
/// it, we do not assume they know the ID of the log.
|
||||
///
|
||||
/// timestamp: The SCT timestamp, in decimal.
|
||||
///
|
||||
/// extensions: An opaque type for future expansion. It is likely
|
||||
/// that not all participants will need to understand data in this
|
||||
/// field. Logs should set this to the empty string. Clients
|
||||
/// should decode the base64-encoded data and include it in the
|
||||
/// SCT.
|
||||
///
|
||||
/// signature: The SCT signature, base64 encoded.
|
||||
///
|
||||
/// If the "sct_version" is not v1, then a v1 client may be unable to
|
||||
/// verify the signature. It MUST NOT construe this as an error. (Note:
|
||||
/// Log clients don't need to be able to verify this structure; only TLS
|
||||
/// clients do. If we were to serve the structure as a binary blob, then
|
||||
/// we could completely change it without requiring an upgrade to v1
|
||||
/// clients.)
|
||||
/// ```
|
||||
pub const ADD_CHAIN_ENDPOINT: Endpoint = (reqwest::Method::POST, "/ct/v1/add-chain");
|
47
src/api/mod.rs
Normal file
47
src/api/mod.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use reqwest::Url;
|
||||
use responses::{AddChainRequest, AddChainResponse};
|
||||
|
||||
pub mod endpoints;
|
||||
pub mod responses;
|
||||
|
||||
pub struct CtApiClient {
|
||||
inner_client: reqwest::Client,
|
||||
log_url: Url
|
||||
}
|
||||
|
||||
impl CtApiClient {
|
||||
pub fn new(log_url: Url) -> reqwest::Result<Self> {
|
||||
Ok(Self {
|
||||
inner_client: reqwest::Client::builder().https_only(true).build()?,
|
||||
log_url
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds a chain to the CT log.
|
||||
///
|
||||
/// See: [`endpoints::ADD_CHAIN_ENDPOINT`]
|
||||
///
|
||||
/// ## 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.
|
||||
/// Specifically, compliant CT logs will reject chains that do not verify
|
||||
/// sequentially from the first entry (end-user certificate) to the last
|
||||
/// entry (a trusted root or an intermediate signed by the a trusted root).
|
||||
pub async fn add_chain(
|
||||
&self,
|
||||
chain: Vec<String>
|
||||
) -> reqwest::Result<AddChainResponse> {
|
||||
self.inner_client
|
||||
.request(
|
||||
endpoints::ADD_CHAIN_ENDPOINT.0,
|
||||
endpoints::ADD_CHAIN_ENDPOINT.1
|
||||
)
|
||||
.json(&AddChainRequest { chain })
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.await
|
||||
}
|
||||
}
|
21
src/api/responses.rs
Normal file
21
src/api/responses.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A request payload for adding a chain to a CT log
|
||||
///
|
||||
/// See: [`super::endpoints::ADD_CHAIN_ENDPOINT`]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AddChainRequest {
|
||||
pub chain: Vec<String>
|
||||
}
|
||||
|
||||
/// A response given when adding a chain to a CT log
|
||||
///
|
||||
/// See: [`super::endpoints::ADD_CHAIN_ENDPOINT`]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct AddChainResponse {
|
||||
pub sct_version: u8,
|
||||
pub log_id: String,
|
||||
pub timestamp: u64,
|
||||
pub extensions: String,
|
||||
pub signature: String
|
||||
}
|
|
@ -10,5 +10,7 @@
|
|||
//!
|
||||
//! [RFC6926]: https://datatracker.ietf.org/doc/html/rfc6962
|
||||
|
||||
#[cfg(feature = "api")]
|
||||
pub mod api;
|
||||
pub mod merkle;
|
||||
pub mod parsing;
|
||||
|
|
Loading…
Reference in a new issue