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}