we are so back (first real commit)

This commit is contained in:
ForeverPyrite
2025-09-19 21:34:09 -04:00
parent 83c7e7402d
commit a3176b6760
13 changed files with 880 additions and 0 deletions

33
aws_sigv4/Cargo.toml Normal file
View File

@@ -0,0 +1,33 @@
[package]
name = "aws_sigv4"
version = "0.1.0"
edition = "2024"
[lib]
[dependencies]
# For the very specific date-time format Amazon wants
chrono = "0.4.42"
# SHA256 Hashing
sha2 = "0.10.9"
# Representing binary data in valid strings
hex = "0.4.3"
# Used to derive the signing key and sign the string
hmac = "0.12.1"
# For the HeaderMap struct and Method enum
http = "1.3.1"
# The base URL encoding
urlencoding = "2.1.3"
# Logging (final result is traced with log::trace!)
log = "0.4.28"
[dev-dependencies]
# To get env vars for test
dotenv = "0.15.0"
# For the client
reqwest = { version = "0.12.23", features = ["blocking"] }
# For the Uri, unfortunately (why wouldn't reqwest re-export that?)
# Perhaps it's Url type works fine, but still, I would like to let
# users have as much agency over their http client as possible.
http = "1.3.1"

78
aws_sigv4/README.md Normal file
View File

@@ -0,0 +1,78 @@
# aws_signing library
Yeah so this is a library for signing things with AWS SigV4.
Pretty straightforward.
In fact, there is are only 3 methods on the public API
1. to make the client
2. Change the client region (cause why not?)
3. To transform an unsigned request that will be sent to an AWS compatible server to one with proper SigV4 headers
(With this wording, should I take a mutable reference to headers instead of...hmmm, we will see if I get bored enough to improve it myself.)
If I'm being completely honest, this was part of my own `r2client` that I wrote, which is uses S3
So I don't imagine that I come back to this to actually make a proper AWS Signing library.
I could imagine it being useful to *someone* who also wants to make an AWS abstraction that doesn't have 2 **BILLION** dependences.
## Usage
wait that's what the doc comments and `cargo doc` command is for lmao,
guess I'll get rid of this block
### Unexpected Errors
If you're getting errors related to invalid signatures and you're thinking "It's probably that damn crate I'm using from that newbie!",
you're probably right.
If you got here because crates.io search brought you here, nice.
Didn't mean for that to happen, sorry!
There are much more developed options out there, like [reqsign](https://github.com/apache/opendal-reqsign), which will also handle more
of the service related things for you.
Alternatively, you can try to stick with this crate (mistake)
Using log level trace with whatever logging crate you use will print out all steps of the AWS signing process, so you can follow along
with your favorite documentation/examples and figure out where things go wrong.
With this you can either raise an issue, or review the code yourself to implement a working solution.
## Todo
- [ ] Create unit tests
- [ ] Add option for session keys
- [ ] Additional client or whatever for weirdos who want to use SigV4a
- [ ] Perhaps drop `http` as a dependency?
- [ ] Alternatively, have users pass a mutable reference to a HeaderMap instead of cloning and returning a new one.
- This can kinda hurt ease of use...maybe, but I feel like the slight performance gain and easier management of custom headers makes it worth.
- That's also acting like the crate is mature enough to do anything but provide the minimal headers for SigV4
- [ ] SigV4a
- Is this even really used?
## Contributing
What.
This crate is supposed to be a really simple way to sign AWS request headers and nothing else.
Again, I created it for my Cloudflare R2 (AWS S3) client and I imagine there are a few gaps for other services,
as well as some exceptions to the general programmatic things I saw in the documentation.
If you'd like to help improve it, maybe to make sign requests for some other AWS service, then I'd
appreciate if as specific unit test was added to ensure functionality.
This should be along with the other unit tests remaining intact and passing (unless they are blatantly incompatible
with some AWS documentation)
The code base (one lib.rs file) is pretty straightforward, it very directly follows the outline that AWS's SigV4 documentation provides,
explicitly containing all prerequisite functions and having a function call for all 5 of the steps.
I did this since the other examples were hard to follow, just a chunk of code that doesn't explain itself at any point.
All prerequisite functions, along with functions that don't require any of the keys/credential fields (in SigV4Credentials)
are defined outside of the struct.
Gl;hf
# welp
figure I'd try my hand at writing some public stuff like this, even though I'm not planing on publishing this publicly, only
compiled in the r2client.
I feel...okay about it.
I still feel like this stuff will be so small no matter what that I can still put a little personality into it.
I am incredibly verbose, shocking I know, but I think that's a bad thing.
I should be more brief, and then more verbose when relevant within the code.
Outside of that though, I think this is...fine.
I just need to get better at less yapping.
I really think this crate is too intermingled with the R2 Client to be useful to anyone else, really, although I think this was
*fine* for organizational purposes.
And who knows.
Maybe I'll need it again.

230
aws_sigv4/examples.md Normal file
View File

@@ -0,0 +1,230 @@
Example: GET Object
The following example gets the first 10 bytes of an object (test.txt) from examplebucket. For more information about the API action, see GetObject.
```
```
GET /test.txt HTTP/1.1
Host: examplebucket.s3.amazonaws.com
Authorization: SignatureToBeCalculated
Range: bytes=0-9
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date: 20130524T000000Z
Because this GET request does not provide any body content, the x-amz-content-sha256 value can either be the hash of the empty request body or the literal string "UNSIGNED-PAYLOAD". The following steps show signature calculations and construction of the Authorization header using the hash of an empty string.
```
```
1. StringToSign
a. CanonicalRequest
```
GET
/test.txt
host:examplebucket.s3.amazonaws.com
range:bytes=0-9
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20130524T000000Z
host;range;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```
In the canonical request string, the last line is the hash of the empty request body. The third line is empty because there are no query parameters in the request.
```
```
b. StringToSign
```
AWS4-HMAC-SHA256
20130524T000000Z
20130524/us-east-1/s3/aws4_request
7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972
```
2. SigningKey
```
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")
```
3. Signature
```
f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41
```
4. Authorization header
The resulting Authorization header is as follows:
```
AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;range;x-amz-content-sha256;x-amz-date,Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41
```
Example: PUT Object
This example PUT request creates an object (test$file.text) in examplebucket . The example assumes the following:
You are requesting REDUCED_REDUNDANCY as the storage class by adding the x-amz-storage-class request header. For information about storage classes, see Storage Classes in the Amazon Simple Storage Service User Guide.
The content of the uploaded file is a string, "Welcome to Amazon S3." The value of x-amz-content-sha256 in the request is based on this string.
For information about the API action, see PutObject.
PUT test$file.text HTTP/1.1
Host: examplebucket.s3.amazonaws.com
Date: Fri, 24 May 2013 00:00:00 GMT
Authorization: SignatureToBeCalculated
x-amz-date: 20130524T000000Z
x-amz-storage-class: REDUCED_REDUNDANCY
x-amz-content-sha256: 44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072
<Payload>
The following steps show signature calculations.
StringToSign
CanonicalRequest
PUT
/test%24file.text
date:Fri, 24 May 2013 00:00:00 GMT
host:examplebucket.s3.amazonaws.com
x-amz-content-sha256:44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072
x-amz-date:20130524T000000Z
x-amz-storage-class:REDUCED_REDUNDANCY
date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class
44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072
In the canonical request, the third line is empty because there are no query parameters in the request. The x-amz-content-sha256 canonical header may optionally be signed since its payload hash is already provided at the bottom of the request. The last line is the hash of the body, which should be the same as the x-amz-content-sha256 header value sent to S3 in the HTTP request.
StringToSign
AWS4-HMAC-SHA256
20130524T000000Z
20130524/us-east-1/s3/aws4_request
9e0e90d9c76de8fa5b200d8c849cd5b8dc7a3be3951ddb7f6a76b4158342019d
SigningKey
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")
Signature
98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd
Authorization header
The resulting Authorization header is as follows:
AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=date;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class,Signature=98ad721746da40c64f1a55b78f14c238d841ea1380cd77a1b5971af0ece108bd
Example: GET Bucket Lifecycle
The following GET request retrieves the lifecycle configuration of examplebucket. For information about the API action, see GetBucketLifecycleConfiguration.
GET ?lifecycle HTTP/1.1
Host: examplebucket.s3.amazonaws.com
Authorization: SignatureToBeCalculated
x-amz-date: 20130524T000000Z
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Because the request does not provide any body content, the x-amz-content-sha256 header value is the hash of the empty request body. The following steps show signature calculations.
StringToSign
CanonicalRequest
GET
/
lifecycle=
host:examplebucket.s3.amazonaws.com
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20130524T000000Z
host;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
In the canonical request, the last line is the hash of the empty request body.
StringToSign
AWS4-HMAC-SHA256
20130524T000000Z
20130524/us-east-1/s3/aws4_request
9766c798316ff2757b517bc739a67f6213b4ab36dd5da2f94eaebf79c77395ca
SigningKey
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")
Signature
fea454ca298b7da1c68078a5d1bdbfbbe0d65c699e0f91ac7a200a0136783543
Authorization header
The resulting Authorization header is as follows:
AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=fea454ca298b7da1c68078a5d1bdbfbbe0d65c699e0f91ac7a200a0136783543
Example: Get Bucket (List Objects)
The following example retrieves a list of objects from examplebucket bucket. For information about the API action, see ListObjects.
GET ?max-keys=2&prefix=J HTTP/1.1
Host: examplebucket.s3.amazonaws.com
Authorization: SignatureToBeCalculated
x-amz-date: 20130524T000000Z
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Because the request does not provide a body, the value of x-amz-content-sha256 is the hash of the empty request body. The following steps show signature calculations.
StringToSign
CanonicalRequest
GET
/
max-keys=2&prefix=J
host:examplebucket.s3.amazonaws.com
x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date:20130524T000000Z
host;x-amz-content-sha256;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
In the canonical string, the last line is the hash of the empty request body.
StringToSign
AWS4-HMAC-SHA256
20130524T000000Z
20130524/us-east-1/s3/aws4_request
df57d21db20da04d7fa30298dd4488ba3a2b47ca3a489c74750e0f1e7df1b9b7
SigningKey
signing key = HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("AWS4" + "<YourSecretAccessKey>","20130524"),"us-east-1"),"s3"),"aws4_request")
Signature
34b48302e7b5fa45bde8084f4b7868a86f0a534bc59db6670ed5711ef69dc6f7
Authorization header
The resulting Authorization header is as follows:
AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=34b48302e7b5fa45bde8084f4b7868a86f0a534bc59db6670ed5711ef69dc6f7

390
aws_sigv4/src/lib.rs Normal file
View File

@@ -0,0 +1,390 @@
use chrono::Utc;
use hmac::{Hmac, Mac};
use log::trace;
use sha2::{Digest, Sha256};
type Hmac256 = Hmac<Sha256>;
const EMPTY_PAYLOAD_HASH: &str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
// --- Utility functions ---
fn lowercase(string: &str) -> String {
string.to_lowercase()
}
fn hex<T: AsRef<[u8]>>(data: T) -> String {
hex::encode(data)
}
fn sha256hash<T: AsRef<[u8]>>(data: T) -> [u8; 32] {
Sha256::digest(data).into()
}
fn hmac_sha256(signing_key: &[u8], message: &str) -> Vec<u8> {
let mut mac = Hmac256::new_from_slice(signing_key).expect("bad key :pensive:");
mac.update(message.as_bytes());
mac.finalize().into_bytes().to_vec()
}
fn trim(string: &str) -> String {
string.trim().to_string()
}
pub fn hash<T: AsRef<[u8]>>(payload: T) -> String {
hex(sha256hash(payload))
}
pub fn url_encode(url: &str) -> String {
let mut url = urlencoding::encode(url).into_owned();
let encoded_to_replacement: [(&str, &str); 4] =
[("+", "%20"), ("*", "%2A"), ("%7E", "~"), ("%2F", "/")];
for (encoded_chars_pattern, replacement) in encoded_to_replacement {
url = url.replace(encoded_chars_pattern, replacement)
}
url
}
// --- Signing Functions ---
// These don't use any parts of the SigV4Credentials, so they are external
// --- Canonical request ---
fn create_canonical_request(
method: http::Method,
uri: http::Uri,
mut headers: Vec<(String, String)>,
hashed_payload: &str,
) -> (String, Vec<(String, String)>, String) {
// HTTPMethod
let http_method = method.to_string();
// CanonicalURI = *path only* (spec forbids scheme+host here)
let canonical_uri = if uri.path().is_empty() {
"/".to_string()
} else {
uri.path().to_string()
};
// CanonicalQueryString (URL-encoded, sorted by key)
let canonical_query_string = if let Some(query_string) = uri.query() {
let mut pairs = query_string
.split('&')
.map(|query| {
let (k, v) = query.split_once('=').unwrap_or((query, ""));
(url_encode(k), url_encode(v))
})
.collect::<Vec<_>>();
pairs.sort_by(|a, b| a.0.cmp(&b.0));
pairs
.into_iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join("&")
} else {
String::new()
};
// checks for proper host headers
let host = uri
.host()
.expect("uri passed without a proper host")
.to_string();
if !headers.iter().any(|(k, _)| k.eq_ignore_ascii_case("host")) {
headers.push(("host".to_string(), host));
}
if !headers
.iter()
.any(|(k, _)| k.eq_ignore_ascii_case("x-amz-content-sha256"))
{
headers.push((
"x-amz-content-sha256".to_string(),
hashed_payload.to_owned(),
))
}
// CanonicalHeaders + SignedHeaders
let mut http_headers = headers
.iter()
.map(|(name, value)| (lowercase(name), trim(value)))
.collect::<Vec<_>>();
http_headers.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
let canonical_headers: String = http_headers
.iter()
.map(|(k, v)| format!("{k}:{v}\n"))
.collect();
let signed_headers: String = http_headers
.iter()
.map(|(k, _)| k.clone())
.collect::<Vec<_>>()
.join(";");
// Final canonical request
let canonical_request = format!(
"{http_method}\n{canonical_uri}\n{canonical_query_string}\n{canonical_headers}\n{signed_headers}\n{hashed_payload}"
);
(canonical_request, http_headers, signed_headers)
}
/// This is really an superfluous wrapper to the hmac_sha256 function.
/// Since it was it's own step in AWS's documentation, I initially gave it it's own function.
/// However I think a comment over calculate_signature could do now.
fn calculate_signature(signing_key: &[u8], string_to_sign: &str) -> Vec<u8> {
hmac_sha256(signing_key, string_to_sign)
}
fn string_to_sign(scope: &str, amz_date: &str, hashed_canonical_request: &str) -> String {
format!(
"{}\n{}\n{}\n{}",
"AWS4-HMAC-SHA256", amz_date, scope, hashed_canonical_request
)
}
/// Structure containing all the data relevant for an AWS Service utilizing SigV4.
/// Service: String containing the AWS service (e.g. "ec2" or "s3")
/// Region: String containing the AWS region you're working in (e.g. "auto" or "us-east-1")
/// Access Key: The "Access Key" to use with the AWS service (crazy, ik)
/// Secret Key: The "Secret Key" that is used for cryptographic signing for the AWS Service (woah)
///
/// ```no_run
/// use aws_signing::SigV4Credentials;
/// use http::{Method, Uri};
///
/// let s3_client = SigV4Credentials::new(
/// "s3",
/// "us-east-1",
/// std::env::var("S3_ACCESS_KEY").unwrap(),
/// std::env::var("S3_SECRET_KEY").unwrap(),
/// );
/// let (_, request_headers) = s3_client.signature(
/// Method::GET,
/// Uri::from_static("https://s3.us-east-1.amazonaws.com/example-bucket/file.txt"),
/// vec![("content-type", "text/plain")],
/// "" // Since it's a GET request, the payload is ""
/// );
/// ```
#[derive(Debug)]
// A more mature client would also have session_key: Option<String>, but not my problem
pub struct SigV4Credentials {
// Would it makes more sense for these to be type generics
// with trait param ToString?
// Either that or just &str or String...wait, union?
// Nah there has to be a better way to do it than...that
// but I don't wanna enum!!!
service: String,
region: String,
access_key: String,
secret_key: String,
}
/// NOTE: This only impliments functions that require one of the SigV4Credentials fields.
/// For other functions related to the signing proccess, they are defined above, including the
/// prequisite functions defined at https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html
impl SigV4Credentials {
/// Creates a new instance of the SigV4Credentials for a particular service, in a region, with your
/// private and public access keys.
///
/// For some reason this function will take any values that impl Into<String>, so you can pass
/// &str, String, or something else if you decide to get freaky.
pub fn new(
service: impl Into<String>,
region: impl Into<String>,
pub_key: impl Into<String>,
priv_key: impl Into<String>,
) -> Self {
Self {
service: service.into(),
region: region.into(),
access_key: pub_key.into(),
secret_key: priv_key.into(),
}
}
// In a more mature client, this might be an enum of AWSRegions
// I also don't even know if this could ever be useful lol, wouldn't you have individual
// clients for each region or use "auto" for AWS to figure it out for you? whatever.
pub fn set_region(&mut self, region: impl Into<String>) {
self.region = region.into()
}
fn credential_scope(&self, date: &str) -> String {
format!(
"{}/{}/{}/aws4_request",
date,
lowercase(&self.region),
lowercase(&self.service)
)
}
fn derive_signing_key(&self, date: &str) -> Vec<u8> {
let secret_key = &self.secret_key;
let key = format!("AWS4{secret_key}");
let date_key = hmac_sha256(key.as_bytes(), date);
let date_region_key = hmac_sha256(&date_key, &self.region);
let date_region_service_key = hmac_sha256(&date_region_key, &self.service);
hmac_sha256(&date_region_service_key, "aws4_request")
}
// --- API ---
/// This is the only function to use <3
pub fn signature<T: AsRef<[u8]>>(
&self,
method: http::Method,
uri: http::Uri,
// Should probably make this a header map, then turn it into a Vec(String, String) to sort
// by header name cause Amazon said so.
mut headers: Vec<(String, String)>,
payload: T,
) -> (String, http::HeaderMap) {
let auth_algorithm = "AWS4-HMAC-SHA256";
let now = Utc::now();
let amz_date = now.format("%Y%m%dT%H%M%SZ").to_string();
let date = now.format("%Y%m%d").to_string();
let payload_as_bytes = payload.as_ref();
let payload_hash = if payload_as_bytes.is_empty() {
EMPTY_PAYLOAD_HASH.to_string()
} else {
hash(payload_as_bytes)
};
// Add x-amz-date header if not already present
if !headers
.iter()
.any(|(k, _)| k.eq_ignore_ascii_case("x-amz-date"))
{
headers.push(("x-amz-date".to_string(), amz_date.clone()));
}
// Canonical request
let (canonical_request, mut headers, signed_headers) =
create_canonical_request(method, uri, headers, &payload_hash);
// String to sign
let scope = self.credential_scope(&date);
let hashed_canonical_request = hash(&canonical_request);
let string_to_sign = string_to_sign(&scope, &amz_date, &hashed_canonical_request);
// Signing key + signature
let signing_key = self.derive_signing_key(&date);
let signature = hex(calculate_signature(&signing_key, &string_to_sign));
// Authorization header
let access_key = &self.access_key;
let credential = format!("{access_key}/{scope}");
let auth_header = format!(
"{auth_algorithm} Credential={credential}, SignedHeaders={signed_headers}, Signature={signature}"
);
trace!("\n--- AWS SigV4 Debug ---");
trace!("1. CanonicalRequest:\n---\n{canonical_request}\n---");
trace!("2. StringToSign:\n---\n{string_to_sign}\n---");
trace!("3. SigningKey:\n---\n{}\n---", hex(&signing_key));
trace!("4. Signature:\n---\n{signature}\n---");
trace!("5. Authorization Header:\n---\n{auth_header}\n---");
headers.push(("authorization".to_string(), auth_header));
let mut header_map: http::HeaderMap = http::HeaderMap::new();
for (header, value) in headers.clone() {
header_map.insert(
http::HeaderName::from_lowercase(header.to_lowercase().as_bytes()).unwrap(),
http::HeaderValue::from_str(&value).unwrap(),
);
}
(signature, header_map)
}
}
#[cfg(test)]
mod tests {
use dotenv::dotenv;
use super::*;
// TODO: Since I can't figure out a problem, I figure the best way to approach this is to use
// test driven development.
// Given a certain input, you should have a specific output after all, so it should make sense
//
// I went ahead and added the 5 major steps
// If one doesn't work, it would make sense to break it into smaller unit tests, but for now
// these five should do.
//
// This link and examples.md should help out a LOT
// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
//
// Alternatively give up and see if "reqsign" will work better.
//
// INFO: THERE IS NO PROBLEM!!!!
// Sike I lied.
// It still makes sense to do unit tests for things like session keys which I haven't
// implimented yet, to make it usable for other clients.
#[test]
fn canonical_request() {
todo!()
}
#[test]
fn canonical_request_hash() {
todo!();
}
#[test]
fn string_to_sign() {
todo!()
}
#[test]
fn derive_signing_key() {
todo!();
}
#[test]
fn calculate_signature() {
todo!()
}
#[test]
fn add_sig_to_req() {
todo!()
}
fn create_client() -> SigV4Credentials {
SigV4Credentials::new(
"s3",
"us-east-1",
std::env::var("AWS_ACCESS_KEY").unwrap_or("AKIAIOSFODNN7EXAMPLE".to_string()),
std::env::var("AWS_SECRET_KEY")
.unwrap_or("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".to_string()),
)
}
#[test]
fn s3_upload_test() {
// Initialize .env and vars
dotenv().ok();
let bucket = std::env::var("S3_BUCKET").unwrap();
let file_key = url_encode("s3-sigv4-test.txt");
// "Use from parts!!" no.
let url = format!(
"{}/{}/{}",
std::env::var("S3_ENDPOINT").unwrap(),
bucket,
file_key
);
let endpoint: http::Uri = url.parse().unwrap();
let headers = vec![("host".to_string(), endpoint.host().unwrap().to_owned())];
let signer = create_client();
let (_, header_map) = signer.signature(http::Method::GET, endpoint, headers, b"");
let res = reqwest::blocking::Client::new()
.get(&url)
.headers(header_map)
.send()
.unwrap();
let status = res.status();
assert!(status.is_success())
}
}