truelayer_signing/lib.rs
1//! Produce & verify TrueLayer API `Tl-Signature` request headers.
2mod base64;
3mod http;
4mod jws;
5mod openssl;
6mod sign;
7mod verify;
8
9pub use http::Method;
10pub use jws::{JwsAlgorithm, JwsHeader, TlVersion};
11pub use sign::{CustomSigner, Signer, SignerBuilder};
12use verify::PublicKey;
13pub use verify::{CustomVerifier, Verifier, VerifierBuilder};
14
15/// A utility unit type to denote an item hasn't been set.
16pub struct Unset;
17
18/// Start building a request `Tl-Signature` header value using private key
19/// pem data & the key's `kid`.
20///
21/// # Example
22/// ```no_run
23/// # fn main() -> Result<(), truelayer_signing::Error> {
24/// # let (kid, private_key, idempotency_key, body) = unimplemented!();
25/// let tl_signature = truelayer_signing::sign_with_pem(kid, private_key)
26/// .method(truelayer_signing::Method::Post)
27/// .path("/payouts")
28/// .header("Idempotency-Key", idempotency_key)
29/// .body(body)
30/// .build_signer()
31/// .sign()?;
32/// # Ok(()) }
33/// ```
34pub fn sign_with_pem<'a>(
35 kid: &'a str,
36 private_key_pem: &'a [u8],
37) -> SignerBuilder<'a, &'a str, &'a [u8], Unset, Unset, Unset> {
38 SignerBuilder::build_with_pem(kid, private_key_pem)
39}
40
41/// Start building a `Tl-Signature` header verifier using public key pem data.
42///
43/// # Example
44/// ```no_run
45/// # fn main() -> Result<(), truelayer_signing::Error> {
46/// # let (public_key, idempotency_key, body, tl_signature) = unimplemented!();
47/// truelayer_signing::verify_with_pem(public_key)
48/// .method(truelayer_signing::Method::Post)
49/// .path("/payouts")
50/// .require_header("Idempotency-Key")
51/// .header("Idempotency-Key", idempotency_key)
52/// .body(body)
53/// .build_verifier()
54/// .verify(tl_signature)?;
55/// # Ok(()) }
56/// ```
57pub fn verify_with_pem(
58 public_key_pem: &[u8],
59) -> VerifierBuilder<'_, PublicKey<'_>, Unset, Unset, Unset> {
60 VerifierBuilder::pem(public_key_pem)
61}
62
63/// Start building a `Tl-Signature` header verifier using public key JWKs JSON response data.
64///
65/// See <https://datatracker.ietf.org/doc/html/rfc7517>.
66///
67/// # Example
68/// ```no_run
69/// # fn main() -> Result<(), truelayer_signing::Error> {
70/// # let (jwks, body, tl_signature) = unimplemented!();
71/// # let headers: Vec<(&str, &[u8])> = unimplemented!();
72/// // jwks json of form: {"keys":[...]}
73/// truelayer_signing::verify_with_jwks(jwks)
74/// .method(truelayer_signing::Method::Post)
75/// .path("/webhook")
76/// .headers(headers)
77/// .body(body)
78/// .build_verifier()
79/// .verify(tl_signature)?;
80/// # Ok(()) }
81/// ```
82pub fn verify_with_jwks(jwks: &[u8]) -> VerifierBuilder<'_, PublicKey<'_>, Unset, Unset, Unset> {
83 VerifierBuilder::jwks(jwks)
84}
85
86/// Extract [`JwsHeader`] info from a `Tl-Signature` header value.
87///
88/// This can then be used to pick a verification key using the `kid` etc.
89pub fn extract_jws_header(tl_signature: &str) -> Result<JwsHeader, Error> {
90 Ok(verify::parse_tl_signature(tl_signature)?.header)
91}
92
93/// Sign/verification error.
94#[derive(Debug, thiserror::Error)]
95pub enum Error {
96 /// Key data is invalid.
97 #[error("invalid key: {0}")]
98 InvalidKey(anyhow::Error),
99 /// JWS signature generation or verification failed.
100 #[error("jws signing/verification failed: {0}")]
101 JwsError(anyhow::Error),
102 /// Other error.
103 #[error("Error: {0}")]
104 Other(anyhow::Error),
105}