cloud_sdk/authentication/signing/
output.rs1use core::fmt;
2
3use cloud_sdk_sanitization::sanitize_bytes;
4
5use crate::transport::TransportRequest;
6
7use super::SigningContext;
8
9pub trait RequestSigner {
11 type Error;
13
14 fn sign(&self, input: &[u8], output: &mut [u8]) -> Result<usize, Self::Error>;
16}
17
18pub enum SigningOutputError<E> {
20 Signer(E),
22 Empty,
24 TooLong,
26}
27
28impl<E> fmt::Debug for SigningOutputError<E> {
29 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
30 formatter.write_str(match self {
31 Self::Signer(_) => "SigningOutputError::Signer([redacted])",
32 Self::Empty => "SigningOutputError::Empty",
33 Self::TooLong => "SigningOutputError::TooLong",
34 })
35 }
36}
37
38impl<E> fmt::Display for SigningOutputError<E> {
39 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40 formatter.write_str(match self {
41 Self::Signer(_) => "request signer rejected the canonical input",
42 Self::Empty => "request signer returned empty output",
43 Self::TooLong => "request signer returned an out-of-bounds length",
44 })
45 }
46}
47
48impl<E> core::error::Error for SigningOutputError<E>
49where
50 E: core::error::Error + 'static,
51{
52 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
53 match self {
54 Self::Signer(error) => Some(error),
55 Self::Empty | Self::TooLong => None,
56 }
57 }
58}
59
60pub struct SignedRequest<'output, 'request> {
62 request: TransportRequest<'request>,
63 context: SigningContext<'request>,
64 storage: &'output mut [u8],
65 len: usize,
66}
67
68impl<'output, 'request> SignedRequest<'output, 'request> {
69 pub(super) fn sign<S: RequestSigner>(
70 request: TransportRequest<'request>,
71 context: SigningContext<'request>,
72 input: &[u8],
73 signer: &S,
74 output: &'output mut [u8],
75 ) -> Result<Self, SigningOutputError<S::Error>> {
76 sanitize_bytes(output);
77 let mut signed = Self {
78 request,
79 context,
80 storage: output,
81 len: 0,
82 };
83 let len = signer
84 .sign(input, signed.storage)
85 .map_err(SigningOutputError::Signer)?;
86 if len == 0 {
87 return Err(SigningOutputError::Empty);
88 }
89 if len > signed.storage.len() {
90 return Err(SigningOutputError::TooLong);
91 }
92 signed.len = len;
93 Ok(signed)
94 }
95
96 #[must_use]
98 pub const fn request(&self) -> TransportRequest<'request> {
99 self.request
100 }
101
102 #[must_use]
104 pub const fn context(&self) -> SigningContext<'request> {
105 self.context
106 }
107
108 #[must_use]
110 pub fn signature(&self) -> &[u8] {
111 self.storage.get(..self.len).unwrap_or_default()
112 }
113}
114
115impl fmt::Debug for SignedRequest<'_, '_> {
116 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
117 formatter
118 .debug_struct("SignedRequest")
119 .field("request", &self.request)
120 .field("context", &self.context)
121 .field("signature_len", &self.len)
122 .field("signature", &"[redacted]")
123 .finish()
124 }
125}
126
127impl Drop for SignedRequest<'_, '_> {
128 fn drop(&mut self) {
129 sanitize_bytes(self.storage);
130 }
131}