use std::{fmt, time::Duration};
#[cfg(any(test, feature = "fuzzing"))]
use super::super::canonical_uri;
use super::super::{Header, QueryParam, canonical_uri_from_encoded};
pub(crate) struct SigningCredentials<'a> {
pub(super) access_key_id: &'a str,
pub(super) secret_access_key: &'a [u8],
pub(super) session_token: Option<&'a str>,
}
impl<'a> SigningCredentials<'a> {
pub(crate) const fn new(
access_key_id: &'a str,
secret_access_key: &'a [u8],
session_token: Option<&'a str>,
) -> Self {
Self {
access_key_id,
secret_access_key,
session_token,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct SigningScope<'a> {
pub(crate) region: &'a str,
pub(crate) service: &'a str,
}
impl<'a> SigningScope<'a> {
pub(crate) const fn new(region: &'a str, service: &'a str) -> Self {
Self { region, service }
}
}
#[derive(Clone, Copy)]
pub(crate) enum SigningPath<'a> {
#[cfg(any(test, feature = "fuzzing"))]
Raw(&'a str),
Encoded(&'a str),
}
impl<'a> SigningPath<'a> {
#[cfg(any(test, feature = "fuzzing"))]
pub(crate) const fn raw(path: &'a str) -> Self {
Self::Raw(path)
}
pub(crate) const fn encoded(path: &'a str) -> Self {
Self::Encoded(path)
}
pub(super) fn canonical(self) -> Result<String, SigningError> {
match self {
#[cfg(any(test, feature = "fuzzing"))]
Self::Raw(path) => Ok(canonical_uri(path)),
Self::Encoded(path) => canonical_uri_from_encoded(path),
}
}
}
pub(crate) struct HeaderSigningRequest<'a> {
pub(crate) method: &'a str,
pub(crate) uri_path: SigningPath<'a>,
pub(crate) query: &'a [QueryParam<'a>],
pub(crate) headers: &'a [Header<'a>],
pub(crate) payload_hash: &'a str,
}
pub(crate) struct HeaderSigningOutput {
pub(super) authorization: String,
pub(super) amz_date: String,
pub(super) security_token: Option<String>,
}
impl HeaderSigningOutput {
pub(crate) fn authorization(&self) -> &str {
&self.authorization
}
pub(crate) fn amz_date(&self) -> &str {
&self.amz_date
}
pub(crate) fn security_token(&self) -> Option<&str> {
self.security_token.as_deref()
}
}
pub(crate) struct PresigningRequest<'a> {
pub(crate) method: &'a str,
pub(crate) uri_path: SigningPath<'a>,
pub(crate) query: &'a [QueryParam<'a>],
pub(crate) headers: &'a [Header<'a>],
pub(crate) expires: Duration,
pub(crate) payload_hash: Option<&'a str>,
}
pub(crate) struct PresignedQuery(pub(super) String);
impl PresignedQuery {
pub(crate) fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum SigningError {
InvalidCredentials,
InvalidScope,
InvalidMethod,
InvalidHeaderName,
InvalidHeaderValue,
MissingHostHeader,
InvalidHostHeader,
InvalidPayloadHash,
InvalidEncodedUri,
ReservedHeader,
ReservedQueryParameter,
InvalidExpiry,
UnsupportedPresignMethod,
Cryptographic,
Timestamp,
}
impl fmt::Display for SigningError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::InvalidCredentials => "invalid signing credentials",
Self::InvalidScope => "invalid signing scope",
Self::InvalidMethod => "invalid HTTP method for signing",
Self::InvalidHeaderName => "invalid signed header name",
Self::InvalidHeaderValue => "invalid signed header value",
Self::MissingHostHeader => "a host header is required for signing",
Self::InvalidHostHeader => "the host header is empty or ambiguous",
Self::InvalidPayloadHash => "invalid SigV4 payload hash",
Self::InvalidEncodedUri => "URL path contains invalid percent encoding",
Self::ReservedHeader => "a signing-owned header was supplied",
Self::ReservedQueryParameter => "a signing-owned query parameter was supplied",
Self::InvalidExpiry => "presigning expiry must be between 1 second and 7 days",
Self::UnsupportedPresignMethod => "unsupported HTTP method for S3 presigning",
Self::Cryptographic => "signature calculation failed",
Self::Timestamp => "signing timestamp could not be represented",
})
}
}
impl std::error::Error for SigningError {}