- if you own these devices physically and going to keep the possession of that all the time. You can deliver the access key to those devices after some authentication from your service. Once you have the credentials available on the device - You can make Signed PUT request to Tigris to upload object on to your bucket. (Here I assume you don’t want to use AWS C++ SDK to keep things light). It still uses
libcurl
and openssl
libraries for HTTP calls and signature computation.
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <curl/curl.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
const std::string AWS_REGION = "auto";
const std::string AWS_SERVICE = "s3";
const std::string AWS_ACCESS_KEY = "your-access-key";
const std::string AWS_SECRET_KEY = "your-secret-key";
const std::string BUCKET_NAME = "your-bucket-name";
const std::string OBJECT_KEY = "example.txt";
const std::string FILE_PATH = "path/to/your/file.txt"; // local file path
// Helper function to create a date string in the format required in signature computation
std::string get_current_date() {
time_t now = time(0);
struct tm *tstruct = gmtime(&now);
char buf[80];
strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", tstruct);
return std::string(buf);
}
// Helper function to create a date string for the credential scope
std::string get_aws_date() {
time_t now = time(0);
struct tm *tstruct = gmtime(&now);
char buf[80];
strftime(buf, sizeof(buf), "%Y%m%d", tstruct);
return std::string(buf);
}
// Helper function to generate an HMAC-SHA256 digest
std::string hmac_sha256(const std::string &key, const std::string &data) {
unsigned char *result;
unsigned int len = SHA256_DIGEST_LENGTH;
result = HMAC(EVP_sha256(), key.c_str(), key.length(), (unsigned char*)data.c_str(), data.length(), NULL, &len);
return std::string((char*)result, len);
}
// Function to generate AWS Signature Version 4
std::string generate_signature(const std::string &date, const std::string &credential_scope, const std::string &signed_headers, const std::string &payload_hash, const std::string &canonical_request, const std::string &request) {
std::string secret_key = "AWS4" + AWS_SECRET_KEY;
std::string region_key = hmac_sha256(secret_key, AWS_REGION);
std::string service_key = hmac_sha256(region_key, AWS_SERVICE);
std::string signing_key = hmac_sha256(service_key, "aws4_request");
std::string string_to_sign = "AWS4-HMAC-SHA256\n" + date + "\n" + credential_scope + "\n" + payload_hash;
std::string string_to_sign_hmac = hmac_sha256(signing_key, string_to_sign);
return string_to_sign_hmac;
}
std::string get_signed_headers(const std::string &date, const std::string &signed_headers) {
std::string canonical_headers = "host:s3.amazonaws.com\nx-amz-date:" + date + "\n";
return canonical_headers;
}
int main() {
// Get the current date and time in AWS format
std::string date = get_current_date();
std::string aws_date = get_aws_date();
// Construct the canonical request
std::string canonical_uri = "/" + OBJECT_KEY;
std::string canonical_querystring = "";
std::string canonical_headers = get_signed_headers(date, "host;x-amz-date");
std::string payload_hash = "UNSIGNED-PAYLOAD";
std::string canonical_request = "PUT\n" + canonical_uri + "\n" + canonical_querystring + "\n" + canonical_headers + "\n" + "host;x-amz-date\n" + payload_hash;
// Calculate the signature
std::string credential_scope = aws_date + "/" + AWS_REGION + "/" + AWS_SERVICE + "/aws4_request";
std::string signature = generate_signature(date, credential_scope, "host;x-amz-date", payload_hash, canonical_request, "s3.amazonaws.com");
// Build the authorization header
std::string authorization_header = "AWS4-HMAC-SHA256 Credential=" + AWS_ACCESS_KEY + "/" + credential_scope + ", SignedHeaders=host;x-amz-date, Signature=" + signature;
// Create the HTTP request using cURL
CURL *curl = curl_easy_init();
if (curl) {
// Set the cURL options
curl_easy_setopt(curl, CURLOPT_URL, "https://fly.storage.tigris.dev/" + BUCKET_NAME + "/" + OBJECT_KEY);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
// Set the headers
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, ("Authorization: " + authorization_header).c_str());
headers = curl_slist_append(headers, ("x-amz-date: " + date).c_str());
headers = curl_slist_append(headers, "Content-Type: application/octet-stream");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// Read the file content and set it as the body of the request
std::ifstream file(FILE_PATH, std::ios::binary);
std::stringstream file_stream;
file_stream << file.rdbuf();
std::string file_content = file_stream.str();
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, file_content.c_str());
// Perform the request
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "cURL error: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "Successfully uploaded the file to S3!" << std::endl;
}
// Clean up
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
}
return 0;
}
(note above code isn’t fully tested)