Sign in

    sigv4

    Base package that can be used for v4 signing for AWS and others. Used by kaashyapan/s3.

    aws
    s3
    sigv4
    Download zip
    Version
    0.5.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    50

    #kaashyapan/sigv4

    Used by kaashyapan/s3

    CanonicalParts

    pub(all) struct CanonicalParts {
    canonical_request : String
    string_to_sign : String
    signed_headers : String
    payload_hash : String
    } derive(Eq)

    The canonical-layer intermediates of a SigV4 signing operation, exposed for debugging and for algorithms that share the canonical layer (e.g. SigV4a). canonical_request is the \n-joined creq string, string_to_sign the AWS4-HMAC-SHA256 sts string built from it, and signed_headers / payload_hash the SignedHeaders= value and resolved canonical payload line. For header signing the creq is built from the request's signing headers; for query signing, from the headers that will ride in the URL.

    Credentials

    pub(all) struct Credentials {
    access_key_id : String
    secret_access_key : String
    session_token : String?
    } derive(Eq)

    Credentials::Credentials

    fn Credentials::Credentials(access_key_id : String, secret_access_key : String, session_token? : String) -> Credentials

    HttpRequest

    pub(all) struct HttpRequest {
    meth :
    RequestMethod

    path : String
    query : Array[(String, String?)]
    headers : Array[(String, String)]
    body : Bytes
    } derive(Eq)

    An HTTP request to be signed. meth is the request method, path is the request-target path as sent (URI-encoded once), query holds the query parameters exactly as they will appear on the wire — names and values already RFC 3986 component-encoded (see encode_query_component), duplicates allowed, None for a bare parameter without = — headers are name/value pairs in any order, and body holds the raw payload bytes.

    PayloadChecksumKind

    pub(all) enum PayloadChecksumKind {
    XAmzSha256
    NoHeader
    } derive(Eq)

    Whether the X-Amz-Content-Sha256 header is added to the signed request:

    • NoHeader — "normal mode"; correct for every AWS service except S3 (the default)
    • XAmzSha256 — add the header carrying the resolved payload line; required for S3

    PayloadHash

    pub(all) enum PayloadHash {
    Sha256
    Empty
    Unsigned
    Provided(String)
    } derive(Eq)

    How the payload line of the canonical request is produced:

    • Sha256 — hash body and use it as the payload line (generic services that include the payload hash in the signature)
    • Empty — the hash of the empty body as the payload line
    • Unsigned — the literal UNSIGNED-PAYLOAD line (S3 streaming/chunked uploads and shared download URLs)
    • Provided — a caller-computed hash (hex, or UNSIGNED-PAYLOAD) as the payload line

    The payload line is independent of the X-Amz-Content-Sha256 header — whether that header is added is governed by payload_checksum_kind.

    PercentEncoding

    pub(all) enum PercentEncoding {
    Double
    Single
    } derive(Eq)

    How the canonical request's URI path is encoded. The request path is assumed to be URI-encoded once, as sent:

    • Double — re-encode % as %25 (and any remaining reserved characters); required by most services, which do not decode the path before verifying the signature (the default)
    • Single — take the path as-is; required by S3, which rejects double-encoded paths

    PresignedUrl

    pub(all) struct PresignedUrl {
    url : String
    signed_headers : String
    } derive(Eq)

    The result of signing a request with the SigV4 query-string auth variant. url is the fully signed URL. signed_headers is the SignedHeaders= value (semicolon-joined, lowercase header names): every header listed there must be sent verbatim with the request for the signature to verify.

    SigningOptions

    pub(all) struct SigningOptions {
    timestamp : String
    region : String
    service : String
    normalize_path : Bool
    percent_encoding : PercentEncoding
    payload_hash : PayloadHash
    payload_checksum_kind : PayloadChecksumKind
    omit_session_token : Bool
    excluded_headers : Array[String]
    } derive(Eq)

    Request signing options: a SigV4 timestamp (YYYYMMDDTHHMMSSZ) and the credential scope, plus behavioral switches. normalize_path removes RFC 3986 dot segments before signing and defaults to true — correct for every AWS service except S3, where object keys containing . / .. must survive. omit_session_token keeps a session token out of the signature while still sending it as a request header (SigV4 conformance scenarios). excluded_headers are excluded from the signature (matched case-insensitively) but still sent with the request.

    SigningOptions::SigningOptions

    fn SigningOptions::SigningOptions(timestamp~ : String, region~ : String, service~ : String, normalize_path? : Bool, percent_encoding? : PercentEncoding, payload_hash? : PayloadHash, payload_checksum_kind? : PayloadChecksumKind, omit_session_token? : Bool, excluded_headers? : Array[String]) -> SigningOptions

    SigningOptions::for_s3

    fn SigningOptions::for_s3(timestamp~ : String, region~ : String, service? : String, payload_hash? : PayloadHash, omit_session_token? : Bool, excluded_headers? : Array[String]) -> SigningOptions

    Signing options for S3 and S3-compatible providers (MinIO, Cloudflare R2, Backblaze B2, Wasabi, DigitalOcean Spaces, ...): paths are taken exactly as sent (no dot-segment normalization, no re-encoding) and the X-Amz-Content-Sha256 header is added, as the S3 API requires. service defaults to "s3", the signing name used by every S3-compatible provider; region is provider-specific (e.g. "us-east-1" for AWS, "auto" for R2, the server-configured region for MinIO).

    canonical_parts

    fn canonical_parts(request~ : HttpRequest, credentials~ : Credentials, options~ : SigningOptions) -> CanonicalParts

    Compute the canonical-layer intermediates for header signing: the canonical request and string-to-sign built from the request's signing headers (the prepared headers minus any session token excluded by omit_session_token), the SignedHeaders= value, and the resolved payload line.

    canonical_query_string

    fn canonical_query_string(params : Array[(String, String?)]) -> String

    Render query parameters as the canonical query string: the parameters must already be RFC 3986 component-encoded exactly as they will appear on the wire (use encode_query_component); this sorts them byte-wise by encoded name then value and joins name=value pairs with & (a bare parameter without = canonicalizes to name=). The result is byte-identical to the query line the signer computes the signature over.

    encode_query_component

    fn encode_query_component(s : String) -> String

    Percent-encode a URI query component per RFC 3986: only unreserved characters survive, everything else (non-ASCII as UTF-8) is escaped. This is the encoding the canonical query string uses, so components encoded here are byte-identical to their canonical form.

    percent_encode_path

    fn percent_encode_path(s : String) -> String

    Percent-encode a URI path for a canonical request line: unreserved characters survive, / separators pass through, non-ASCII characters encode as UTF-8. Applied to paths that already carry percent-escapes, this re-encodes them (% becomes %25), i.e. PercentEncoding::Double.

    presign_url

    fn presign_url(request~ : HttpRequest, credentials~ : Credentials, options~ : SigningOptions, expires~ : Int, scheme? : String) -> PresignedUrl raise

    Sign a request with the SigV4 query-string auth variant and return the fully signed URL together with the SignedHeaders value: every header listed in signed_headers (semicolon-joined, lowercase names) must be sent verbatim with the request for the signature to verify. Headers named in excluded_headers are never signed.

    The auth components (X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders, X-Amz-Security-Token when present, and X-Amz-Signature) land in the URL query string rather than headers.

    The payload line follows options.payload_hash; shared-download URLs conventionally use Unsigned, while signing a request against a known body uses Sha256 or Provided.

    Raises if request.headers has no Host header.

    expires is bounded by SigV4 to [1, 604800] (1 second to 7 days); the bound is not enforced here — AWS rejects out-of-range values server-side. Existing query entries are taken as sent and merged with the auth params. scheme defaults to https (use e.g. wss for WebSocket endpoints).

    sign

    fn sign(request : HttpRequest, credentials : Credentials, options : SigningOptions) -> HttpRequest

    Sign an HTTP request with AWS Signature Version 4 and return a new request ready to send as-is: the query parameters sorted into canonical order (render the wire form with canonical_query_string), the signing headers applied — X-Amz-Date, plus X-Amz-Content-Sha256 when payload_checksum_kind is XAmzSha256, plus X-Amz-Security-Token when a session token is present and not omitted — and finally the computed Authorization header, which replaces any caller-supplied Authorization header. Headers named in excluded_headers are sent but not signed.

    signing_key

    fn signing_key(secret~ : String, date~ : String, region~ : String, service~ : String) -> Bytes

    Derive the SigV4 signing key for the given date, region and service.