Skip to main content

asimov_http/
lib.rs

1// This is free and unencumbered software released into the public domain.
2
3//! Shared HTTP integrity and client proxy configuration.
4//!
5//! SHA-256 is the sole digest algorithm (RFC 9530). With `integrity`, this crate
6//! implements RFC 9421 Ed25519 signatures using Iroh's base key types,
7//! HTTP message-component canonicalization, and frame-preserving body adapters.
8//! SHA-256 hashing and digest-field encoding are also available without `std`.
9//!
10//! # Features
11//!
12//! - `integrity` (default): signatures, message contexts, and body adapters;
13//!   implies `std`. Callers previously using only `std` must enable `integrity`
14//!   to retain these APIs.
15//! - `proxy`: proxy options, URL validation, and proxy errors with `no_std` and
16//!   alloc support.
17//! - `system-proxy`: origin-scoped environment/native discovery; implies `std`
18//!   and `proxy`.
19//! - `reqwest`: the origin-scoped reqwest proxy adapter; implies `system-proxy`.
20//! - `hyper`: effective proxy endpoint selection for Hyper connectors, including
21//!   shared bypass handling; implies `system-proxy`.
22//!
23//! Proxy features do not enable integrity or its key dependencies.
24//! Use `default-features = false` to select them independently. Native proxy
25//! readers and client adapters are absent from integrity-only builds.
26//!
27//! Signature processing operates on an explicit message context, independently
28//! of HTTP versions and body types. Servers supply the externally visible target
29//! URI, including trusted proxy routing context, rather than guessing a scheme
30//! from an incoming origin-form URI. See `Message` and `SignatureInput`.
31//!
32//! Streaming bodies deliver provisional data and verify at EOF. Callers must
33//! consume the entire stream and observe its terminal result before treating
34//! the message as authenticated. Dropping a body early never verifies it.
35//! Application authorization and atomic nonce-replay rejection follow successful
36//! signature AND body verification; freshness checks alone do not prevent replay.
37//!
38//! # Buffered requests
39//!
40//! ```no_run
41//! # #[cfg(feature = "integrity")] {
42//! use asimov_http::{
43//!     SecretKey, SignatureInput, SignatureParams, Signer, VerificationPolicy,
44//!     REQUEST_SIGNATURE_TAG, body::sign_request, http::Request,
45//! };
46//!
47//! let key = SecretKey::generate(); // Or load the protocol identity from keyring.
48//! let input = SignatureInput::new(
49//!     VerificationPolicy::request().required_components,
50//!     SignatureParams::fresh(&key.public(), REQUEST_SIGNATURE_TAG)?,
51//! )?;
52//! let signer = Signer::new(key, "asimov", input)?;
53//! let target = "https://example.com/fetch";
54//! let mut request = Request::post(target)
55//!     .header("authorization", "Bearer token")
56//!     .header("accept", "application/jsonl")
57//!     .header("content-type", "application/json")
58//!     .body(br#"{"urls":["example:"]}"#.as_slice())?;
59//! sign_request(&mut request, target, &signer)?;
60//! # }
61//! # Ok::<(), Box<dyn core::error::Error>>(())
62//! ```
63//!
64//! # Streaming responses
65//!
66//! ```no_run
67//! # #[cfg(feature = "integrity")] {
68//! use asimov_http::{
69//!     Component, FieldSection, SecretKey, SignatureInput, SignatureParams,
70//!     Signer, body::SigningBody, http::Response,
71//! };
72//! use bytes::Bytes;
73//! use http_body_util::Full;
74//!
75//! let key = SecretKey::generate();
76//! let input = SignatureInput::new(vec![
77//!     Component::new("@status")?,
78//!     Component::new("content-type")?,
79//!     Component::trailer("content-digest")?,
80//! ], SignatureParams::fresh(&key.public(), "example-response")?)?;
81//! let signer = Signer::new(key, "asimov", input)?;
82//! // Any http_body::Body<Data = Bytes> works, including an unbounded stream.
83//! let response = Response::builder()
84//!     .header("content-type", "application/jsonl")
85//!     .body(Full::new(Bytes::from_static(b"{}\n")))?;
86//! let response = SigningBody::response(
87//!     response, None, Some(signer), FieldSection::Headers,
88//! )?;
89//! // Send with Hyper. Content-Digest and Signature are produced at EOF.
90//! # }
91//! # Ok::<(), Box<dyn core::error::Error>>(())
92//! ```
93
94#![no_std]
95#![forbid(unsafe_code)]
96#![cfg_attr(docsrs, feature(doc_cfg))]
97
98extern crate alloc;
99#[cfg(feature = "std")]
100extern crate std;
101
102mod digest;
103pub use digest::{ContentDigest, DigestOptions, DigestPreference};
104
105#[cfg(feature = "integrity")]
106mod error;
107#[cfg(feature = "integrity")]
108pub use error::Error;
109
110#[cfg(feature = "integrity")]
111mod fields;
112#[cfg(feature = "integrity")]
113mod message;
114#[cfg(feature = "integrity")]
115pub use message::{Component, FieldSection, Message, StructuredFieldType};
116
117#[cfg(feature = "integrity")]
118mod signature;
119#[cfg(feature = "integrity")]
120pub use signature::{
121    REQUEST_SIGNATURE_TAG, SignatureFields, SignatureInput, SignatureParams, Signer,
122    VerificationPolicy, VerifiedSignature, Verifier, key_id,
123};
124
125#[cfg(feature = "integrity")]
126pub use http;
127#[cfg(feature = "integrity")]
128pub use iroh_base::{EndpointId as VerifyingKey, SecretKey};
129
130#[cfg(feature = "integrity")]
131pub mod body;
132
133#[cfg(feature = "proxy")]
134pub mod proxy;