Skip to content

Commit 15372fa

Browse files
committed
MinIO request was successfull!!!
1 parent 5698166 commit 15372fa

File tree

2 files changed

+78
-20
lines changed

2 files changed

+78
-20
lines changed

src/s3cpp/auth.h

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,17 @@ class AWSSigV4Signer {
5050

5151
// To sign
5252
std::string string_to_sign =
53-
std::format("{}\n{}\n{}\n{}\n", hash_algo, request_date,
53+
std::format("{}\n{}\n{}\n{}", hash_algo, timestamp,
5454
credential_scope, hex_cannonical_request);
55-
std::string signature = hex(HMAC_SHA256(deriveSigningKey(request_date), string_to_sign));
55+
std::string signature =
56+
hex(HMAC_SHA256(deriveSigningKey(request_date), SHA256_DIGEST_LENGTH, string_to_sign));
5657

5758
// Build the final auth header value
5859
request.header(
5960
"Authorization",
6061
std::format("{} Credential={}/{}, SignedHeaders={}, Signature={}",
6162
hash_algo, access_key, credential_scope, signed_headers,
62-
hex_cannonical_request, signature));
63+
signature));
6364
}
6465

6566
std::string createCannonicalRequest(HttpRequest &request) {
@@ -71,7 +72,13 @@ class AWSSigV4Signer {
7172
if (size_t bpos = url.find("amazonaws.com"); bpos != std::string::npos) {
7273
uri = url.erase(0, bpos + 13);
7374
} else {
74-
// Assume localhost falls here...
75+
// Assume localhost:XXXX (dirty, sorry :( i know)
76+
size_t path_start = url.find('/', 7);
77+
if (path_start != std::string::npos) {
78+
uri = url.substr(path_start);
79+
} else {
80+
uri = "/";
81+
}
7582
}
7683

7784
// URI Query-string
@@ -112,11 +119,9 @@ class AWSSigV4Signer {
112119
return digest;
113120
}
114121

115-
const unsigned char *HMAC_SHA256(const unsigned char *key,
122+
const unsigned char *HMAC_SHA256(const unsigned char *key, size_t key_len,
116123
const std::string &data) {
117-
unsigned int hashLen;
118-
119-
return HMAC(EVP_sha256(), key, strlen((char *)key),
124+
return HMAC(EVP_sha256(), key, key_len,
120125
reinterpret_cast<const unsigned char *>(data.c_str()),
121126
data.size(), NULL, NULL);
122127
}
@@ -130,7 +135,6 @@ class AWSSigV4Signer {
130135
return ss.str();
131136
}
132137

133-
134138
// Move to S3 client class
135139
// --
136140
// Required by AWS SigV4 to be in ISO8601 format
@@ -164,13 +168,16 @@ class AWSSigV4Signer {
164168
}
165169
}
166170

167-
const unsigned char * deriveSigningKey(const std::string request_date) {
168-
const std::string initial_candidate = "AWS4" + secret_key;
169-
const unsigned char* keyCandidate = reinterpret_cast<const unsigned char*>(initial_candidate.c_str());
170-
const unsigned char* DateKey = HMAC_SHA256(keyCandidate, request_date);
171-
const unsigned char* DateRegionKey = HMAC_SHA256(DateKey, aws_region);
172-
const unsigned char* DateRegionServiceKey = HMAC_SHA256(DateRegionKey, "s3");
173-
const unsigned char* SigningKey = HMAC_SHA256(DateRegionServiceKey, "aws4_request");
171+
const unsigned char *deriveSigningKey(const std::string request_date) {
172+
const std::string initial_candidate = "AWS4" + secret_key;
173+
const unsigned char *keyCandidate =
174+
reinterpret_cast<const unsigned char *>(initial_candidate.c_str());
175+
const unsigned char *DateKey = HMAC_SHA256(keyCandidate, initial_candidate.size(), request_date);
176+
const unsigned char *DateRegionKey = HMAC_SHA256(DateKey, SHA256_DIGEST_LENGTH, aws_region);
177+
const unsigned char *DateRegionServiceKey =
178+
HMAC_SHA256(DateRegionKey, SHA256_DIGEST_LENGTH, "s3");
179+
const unsigned char *SigningKey =
180+
HMAC_SHA256(DateRegionServiceKey, SHA256_DIGEST_LENGTH, "aws4_request");
174181
return SigningKey;
175182
}
176183
};

test/auth_test.cpp

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,39 @@
55
// TODO(cristian): Skip the functional tests that actually query MinIO if its
66
// not up
77

8-
98
TEST(AUTH, SHA256HexDigest) {
109
auto signer = AWSSigV4Signer("minio_access", "minio_secret");
11-
EXPECT_EQ(signer.hex(signer.sha256("github.com/ggcr/s3cpp")), "bc088c51b33c2730707dbb528d1d0bfafc59ba56c8c9aa3b8e0dc0c13e3d9b2b");
10+
EXPECT_EQ(signer.hex(signer.sha256("github.com/ggcr/s3cpp")),
11+
"bc088c51b33c2730707dbb528d1d0bfafc59ba56c8c9aa3b8e0dc0c13e3d9b2b");
1212
}
1313

1414
TEST(AUTH, HMACSHA256HexDigest) {
1515
auto signer = AWSSigV4Signer("minio_access", "minio_secret");
16-
std::string key = "super-secret-key";
17-
EXPECT_EQ(signer.hex(signer.HMAC_SHA256(reinterpret_cast<const unsigned char *>(key.c_str()), "github.com/ggcr/s3cpp")), "558084957fb05bb4786ad6791bfbee71e67a11fea964e5dac6bac6b2f749b339");
16+
std::string key = "super-secret-key";
17+
EXPECT_EQ(signer.hex(signer.HMAC_SHA256(
18+
reinterpret_cast<const unsigned char *>(key.c_str()),
19+
key.size(),
20+
"github.com/ggcr/s3cpp")),
21+
"558084957fb05bb4786ad6791bfbee71e67a11fea964e5dac6bac6b2f749b339");
22+
}
23+
24+
TEST(AUTH, ChainedHMACSHA256HexDigest) {
25+
auto signer = AWSSigV4Signer("minio_access", "minio_secret");
26+
27+
// AWS SigV4 derives the key for the signature using nested HMAC_SHA256
28+
// This test tries to assert that HMAC(HMAC(k, v), v) works as expected
29+
// The hardcoded hashes are taken from: https://emn178.github.io/online-tools/sha256.html
30+
31+
std::string v = "github.com/ggcr/s3cpp";
32+
33+
// HMAC(k, v)
34+
std::string key1 = "super-secret-key";
35+
const unsigned char *hash1 = signer.HMAC_SHA256(reinterpret_cast<const unsigned char *>(key1.c_str()), key1.size(), v);
36+
EXPECT_EQ(signer.hex(hash1), "558084957fb05bb4786ad6791bfbee71e67a11fea964e5dac6bac6b2f749b339");
37+
38+
// HMAC(HMAC(k, v), v)
39+
const unsigned char *hex2 = signer.HMAC_SHA256(hash1, SHA256_DIGEST_LENGTH, v);
40+
EXPECT_EQ(signer.hex(hex2), "d5a2b747dcb6b25cc4da081eedc15edef2d217d8497c67987ed9167d412d898c");
1841
}
1942

2043
TEST(AUTH, CannonicalGETRequest) {
@@ -47,4 +70,32 @@ TEST(AUTH, CannonicalGETRequest) {
4770
host, empty_payload_hash, timestamp, empty_payload_hash);
4871

4972
EXPECT_EQ(signer.createCannonicalRequest(req), expected_canonical);
73+
signer.sign(req);
74+
EXPECT_TRUE(req.getHeaders().contains("Authorization"));
75+
}
76+
77+
TEST(AUTH, MinIOBasicRequest) {
78+
// create signer & http client
79+
auto signer = AWSSigV4Signer("minio_access", "minio_secret");
80+
HttpClient client{};
81+
82+
// prepare request
83+
const std::string host = "127.0.0.1:9000";
84+
const std::string URI = "/";
85+
const std::string URL = std::format("http://{}{}", host, URI);
86+
const std::string timestamp = signer.getTimestamp();
87+
const std::string empty_payload_hash =
88+
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
89+
HttpRequest req = client.get(URL)
90+
.header("Host", host)
91+
.header("X-Amz-Date", timestamp)
92+
.header("X-Amz-Content-Sha256", empty_payload_hash);
93+
signer.sign(req);
94+
HttpResponse resp = req.execute();
95+
96+
EXPECT_EQ(resp.status(), 200);
97+
98+
// std::println("RESPONSE STATUS: {}", resp.status());
99+
// std::println("RESPONSE HEADERS: {}", resp.headers());
100+
// std::println("RESPONSE BODY: {}", resp.body());
50101
}

0 commit comments

Comments
 (0)