Skip to main content

actpub_httpsig/
lib.rs

1//! Dual-stack HTTP message signatures for `ActivityPub`.
2//!
3//! Provides signing and verification for both:
4//!
5//! - [Cavage draft-12][cavage] — the de-facto Fediverse standard (Mastodon,
6//!   Pleroma, Lemmy, Misskey, …)
7//! - [RFC 9421][rfc9421] — the finalized IETF HTTP Message Signatures standard
8//!   (Mastodon 4.5+ accepts both)
9//!
10//! Algorithms supported out of the box:
11//!
12//! - `rsa-sha256` (2048/4096-bit) — legacy main-key format, required for
13//!   interop with current Mastodon
14//! - `ed25519` — FEP-521a Multikey, recommended for new deployments
15//!
16//! All cryptographic primitives are backed by [aws-lc-rs], a memory-safe,
17//! constant-time, FIPS 140-3 validated library maintained by AWS. This crate
18//! is therefore **not** affected by [RUSTSEC-2023-0071] (Marvin Attack) that
19//! impacts the pure-Rust `rsa` crate.
20//!
21//! The crate is HTTP-framework agnostic: it operates on [`http::Request`]
22//! values and leaves transport to the caller.
23//!
24//! # Example — Cavage signing
25//!
26//! ```
27//! # use actpub_httpsig::{SigningKey, CavageSigner, sha256_digest_header};
28//! # use http::{Request, Method};
29//! let key = SigningKey::generate_ed25519();
30//! let body: Vec<u8> = br#"{"type":"Follow"}"#.to_vec();
31//! let mut req = Request::builder()
32//!     .method(Method::POST)
33//!     .uri("https://example.com/inbox")
34//!     .header("host", "example.com")
35//!     .header("date", "Sun, 05 Jan 2014 21:31:40 GMT")
36//!     .header("digest", sha256_digest_header(&body))
37//!     .header("content-type", "application/activity+json")
38//!     .body(body)
39//!     .unwrap();
40//!
41//! let signer = CavageSigner::new(&key, "https://example.com/users/alice#main-key");
42//! signer.sign(&mut req).unwrap();
43//! assert!(req.headers().contains_key("signature"));
44//! ```
45//!
46//! [cavage]: https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12
47//! [rfc9421]: https://www.rfc-editor.org/rfc/rfc9421.html
48//! [aws-lc-rs]: https://docs.rs/aws-lc-rs
49//! [RUSTSEC-2023-0071]: https://rustsec.org/advisories/RUSTSEC-2023-0071.html
50#![cfg_attr(docsrs, feature(doc_cfg))]
51#![allow(
52    clippy::error_impl_error,
53    reason = "`Error` is the idiomatic name for the crate's top-level error enum, matching the `thiserror` convention used pervasively in the Rust ecosystem"
54)]
55#![cfg_attr(
56    test,
57    allow(
58        clippy::indexing_slicing,
59        clippy::panic,
60        clippy::unwrap_used,
61        reason = "JSON / byte field indexing via `[\"key\"]` or `[0]` is ergonomic inside tests, and `panic!` / `unwrap()` are the idiomatic way to assert expectations with a failure message when a fixture is wrong"
62    )
63)]
64
65mod cavage;
66mod content_digest;
67mod digest;
68mod error;
69mod http_shared;
70mod key;
71mod policy;
72mod rfc9421;
73mod verify;
74
75use bytes as _;
76use pkcs8 as _;
77#[cfg(test)]
78use tokio as _;
79use tracing as _;
80use url as _;
81
82pub use self::cavage::{
83    CavageHeaderParams, CavageHeaderSet, CavageSigner, CavageVerified, DEFAULT_HEADER_SET,
84    SIGNATURE_HEADER, cavage_verify, cavage_verify_with_policy,
85};
86pub use self::content_digest::{
87    CONTENT_DIGEST_HEADER, DigestAlgorithm, content_digest_header, content_digest_header_with,
88    verify_any_content_digest_header, verify_content_digest_header,
89};
90pub use self::digest::{SHA256_DIGEST_PREFIX, sha256_digest_header, verify_digest_header};
91pub use self::error::Error;
92pub use self::key::{
93    Algorithm, Ed25519PublicKey, Ed25519SigningKey, Multikey, RsaBits, RsaPublicKey, RsaSigningKey,
94    SigningKey, VerifyingKey,
95};
96pub use self::policy::VerifyPolicy;
97pub use self::rfc9421::{
98    Component, DEFAULT_COMPONENTS as RFC9421_DEFAULT_COMPONENTS, Rfc9421Signer, Rfc9421Verified,
99    SIGNATURE_INPUT_HEADER, SignatureInput, parse_signature_dict, parse_signature_input_dict,
100    rfc9421_verify, rfc9421_verify_with_policy, serialise_signature_dict,
101    serialise_signature_input_dict,
102};
103pub use self::verify::{Verified, verify, verify_with_policy};
104
105/// Crate [`Result`] alias with the default error type set to [`Error`].
106pub type Result<T, E = Error> = core::result::Result<T, E>;