Skip to content

yggdrasil.aws.fs.sigv4

sigv4

AWS Signature Version 4 signing for S3 over pure HTTP.

A small, dependency-free SigV4 signer (S3 flavor) — enough to sign the GET / HEAD / PUT / POST / DELETE requests :class:yggdrasil.aws.fs.path.S3Bucket issues over :class:yggdrasil.http_.session.HTTPSession, without dragging botocore's S3 client onto the data path. botocore is still the credential source (env / profile / SSO / STS / instance-metadata resolution lives there); this module only turns a frozen (access_key, secret_key, token) triple plus a request into the Authorization + x-amz-* headers AWS expects.

S3-specific rules (vs the generic SigV4):

  • the canonical URI is not path-normalized (a/./b and a//b are meaningful S3 keys) and / is left unescaped;
  • the payload hash may be a real SHA-256 or the literal UNSIGNED-PAYLOAD (used for large streamed bodies over TLS, where re-hashing the whole object just to sign it would defeat streaming).

Verified byte-for-byte against botocore.auth.S3SigV4Auth — see tests/test_yggdrasil/test_aws/test_sigv4.py.

SigV4Signer

SigV4Signer(*, region: str, credentials_provider, service: str = 's3')

Signs S3 requests with AWS Signature V4.

Stateless w.r.t. the request — credentials are pulled fresh from credentials_provider on every :meth:sign so a rotating STS token is always current. region / service are fixed per bucket.

sign

sign(
    method: str,
    url: URL,
    *,
    headers: Optional[Mapping[str, str]] = None,
    payload_sha256: str = EMPTY_PAYLOAD_SHA256,
    now: Optional[datetime] = None
) -> "dict[str, str]"

Return the headers to add to method url so it's SigV4-authorized.

The returned dict always carries Host, x-amz-date, x-amz-content-sha256 and Authorization (plus x-amz-security-token when the credentials are temporary). Merge it into the request headers and send as-is.