1use std::time::{SystemTime, UNIX_EPOCH};
4
5use crate::{
6 base::CanonicalRequest,
7 profile::Platform,
8 signature::{
9 NonceError, SignatureInputError, SignatureParams, generate_nonce, signature_field,
10 validate_nonce,
11 },
12};
13
14pub trait Signer {
18 type Error;
20
21 fn platform(&self) -> Platform;
23
24 fn sign(&self, client_data_hash: &[u8; 32]) -> Result<Vec<u8>, Self::Error>;
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct SignedHeaders {
36 pub integrity_token: String,
38 pub signature_input: String,
40 pub signature: String,
42 pub request_binding: String,
44}
45
46impl SignedHeaders {
47 #[must_use]
49 pub fn headers(&self) -> [(&'static str, &str); 3] {
50 [
51 (
52 crate::profile::INTEGRITY_TOKEN_HEADER,
53 &self.integrity_token,
54 ),
55 (
56 crate::profile::SIGNATURE_INPUT_HEADER,
57 &self.signature_input,
58 ),
59 (crate::profile::SIGNATURE_HEADER, &self.signature),
60 ]
61 }
62}
63
64#[derive(Debug, thiserror::Error)]
66pub enum SignError<E> {
67 #[error("failed to generate a nonce")]
69 Randomness(#[source] getrandom::Error),
70 #[error(transparent)]
72 Nonce(#[from] NonceError),
73 #[error(transparent)]
75 Params(#[from] SignatureInputError),
76 #[error("the signer failed")]
78 Signer(#[source] E),
79}
80
81pub fn sign_request<S: Signer>(
90 request: &CanonicalRequest<'_>,
91 integrity_token: &str,
92 signer: &S,
93) -> Result<SignedHeaders, SignError<S::Error>> {
94 let created = SystemTime::now()
95 .duration_since(UNIX_EPOCH)
96 .map_or(0, |elapsed| elapsed.as_secs());
97 let nonce = generate_nonce().map_err(SignError::Randomness)?;
98 sign_request_at(
99 request,
100 integrity_token,
101 i64::try_from(created).unwrap_or(i64::MAX),
102 &nonce,
103 signer,
104 )
105}
106
107pub fn sign_request_at<S: Signer>(
114 request: &CanonicalRequest<'_>,
115 integrity_token: &str,
116 created: i64,
117 nonce: &str,
118 signer: &S,
119) -> Result<SignedHeaders, SignError<S::Error>> {
120 validate_nonce(nonce)?;
121 let params = SignatureParams::new(created, nonce, signer.platform().alg())?;
122 let base = request.signature_base(¶ms, integrity_token);
123 let signature = signer
124 .sign(&base.client_data_hash())
125 .map_err(SignError::Signer)?;
126 Ok(SignedHeaders {
127 integrity_token: integrity_token.to_owned(),
128 signature_input: params.to_field(),
129 signature: signature_field(&signature),
130 request_binding: base.request_binding(),
131 })
132}