mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
AWS: created mini-SDK for VPC routing API.
This commit is contained in:
parent
1a4bff66af
commit
702b3a81b7
236
openvpn/aws/awsrest.hpp
Normal file
236
openvpn/aws/awsrest.hpp
Normal file
@ -0,0 +1,236 @@
|
||||
//
|
||||
// OpenVPN
|
||||
//
|
||||
// Copyright (C) 2012-2016 OpenVPN Technologies, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
// AWS REST API query utilities such as query signing
|
||||
|
||||
#ifndef OPENVPN_AWS_AWSREST_H
|
||||
#define OPENVPN_AWS_AWSREST_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint> // for std::uint8_t
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <time.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/http/urlencode.hpp>
|
||||
#include <openvpn/crypto/digestapi.hpp>
|
||||
#include <openvpn/aws/awscreds.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace AWS {
|
||||
class REST
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(aws_rest_error);
|
||||
|
||||
// 20130524T000000Z
|
||||
static std::string amz_date()
|
||||
{
|
||||
struct tm lt;
|
||||
char buf[64];
|
||||
const time_t t = ::time(nullptr);
|
||||
if (!::gmtime_r(&t, <))
|
||||
throw aws_rest_error("gmtime_r failed");
|
||||
if (!::strftime(buf, sizeof(buf),
|
||||
"%Y%m%dT%H%M%SZ",
|
||||
<))
|
||||
throw aws_rest_error("strftime failed");
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
struct SHA256
|
||||
{
|
||||
std::string to_hex() const
|
||||
{
|
||||
return render_hex(hash, sizeof(hash));
|
||||
}
|
||||
|
||||
std::uint8_t hash[32];
|
||||
};
|
||||
|
||||
static SHA256 hmac_sha256(DigestFactory& digest_factory, const std::string& data, const std::string& key)
|
||||
{
|
||||
SHA256 ret;
|
||||
HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, (const std::uint8_t *)key.c_str(), key.length()));
|
||||
hi->update((const std::uint8_t *)data.c_str(), data.length());
|
||||
hi->final(ret.hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SHA256 hmac_sha256(DigestFactory& digest_factory, const std::string& data, const SHA256& key)
|
||||
{
|
||||
SHA256 ret;
|
||||
HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, key.hash, sizeof(key.hash)));
|
||||
hi->update((const std::uint8_t *)data.c_str(), data.length());
|
||||
hi->final(ret.hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SHA256 sha256(DigestFactory& digest_factory, const std::string& data)
|
||||
{
|
||||
SHA256 ret;
|
||||
DigestInstance::Ptr di(digest_factory.new_digest(CryptoAlgs::SHA256));
|
||||
di->update((const std::uint8_t *)data.c_str(), data.length());
|
||||
di->final(ret.hash);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SHA256 signing_key(DigestFactory& df,
|
||||
const std::string& key,
|
||||
const std::string& date_stamp,
|
||||
const std::string& region_name,
|
||||
const std::string& service_name)
|
||||
{
|
||||
const SHA256 h1 = hmac_sha256(df, date_stamp, "AWS4" + key);
|
||||
const SHA256 h2 = hmac_sha256(df, region_name, h1);
|
||||
const SHA256 h3 = hmac_sha256(df, service_name, h2);
|
||||
const SHA256 h4 = hmac_sha256(df, "aws4_request", h3);
|
||||
return h4;
|
||||
}
|
||||
|
||||
struct KeyValue
|
||||
{
|
||||
KeyValue(std::string key_arg, std::string value_arg)
|
||||
: key(std::move(key_arg)),
|
||||
value(std::move(value_arg))
|
||||
{
|
||||
}
|
||||
|
||||
bool operator<(const KeyValue& rhs) const
|
||||
{
|
||||
return key < rhs.key;
|
||||
}
|
||||
|
||||
std::string uri_encode() const
|
||||
{
|
||||
return URL::encode(key) + '=' + URL::encode(value);
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct Query : public std::vector<KeyValue>
|
||||
{
|
||||
std::string canonical_query_string() const
|
||||
{
|
||||
bool first = true;
|
||||
std::string ret;
|
||||
for (auto &p : *this)
|
||||
{
|
||||
if (!first)
|
||||
ret += '&';
|
||||
ret += p.uri_encode();
|
||||
first = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sort()
|
||||
{
|
||||
std::sort(begin(), end());
|
||||
}
|
||||
};
|
||||
|
||||
struct QueryBuilder
|
||||
{
|
||||
std::string date; // such as "20130524T000000Z"
|
||||
unsigned int expires = 300; // request expiration in seconds
|
||||
std::string region; // such as "us-east-1"
|
||||
std::string service; // such as "s3"
|
||||
std::string method; // such as "GET"
|
||||
std::string host; // such as "ec2.us-west-2.amazonaws.com"
|
||||
std::string uri; // such as "/"
|
||||
Query parms;
|
||||
|
||||
std::string uri_query() const
|
||||
{
|
||||
return uri + '?' + parms.canonical_query_string();
|
||||
}
|
||||
|
||||
std::string url_query() const
|
||||
{
|
||||
return "https://" + host + uri_query();
|
||||
}
|
||||
|
||||
void add_amz_parms(const Creds& creds)
|
||||
{
|
||||
parms.emplace_back("X-Amz-Date", date);
|
||||
parms.emplace_back("X-Amz-Expires", std::to_string(expires));
|
||||
parms.emplace_back("X-Amz-Credential", creds.access_key + '/' + amz_credential());
|
||||
parms.emplace_back("X-Amz-Algorithm", "AWS4-HMAC-SHA256");
|
||||
parms.emplace_back("X-Amz-SignedHeaders", amz_signed_headers());
|
||||
}
|
||||
|
||||
void sort_parms()
|
||||
{
|
||||
parms.sort();
|
||||
}
|
||||
|
||||
void add_amz_signature(DigestFactory& digest_factory, const Creds& creds)
|
||||
{
|
||||
parms.emplace_back("X-Amz-Signature", signature(digest_factory, creds));
|
||||
}
|
||||
|
||||
std::string signature(DigestFactory& digest_factory, const Creds& creds) const
|
||||
{
|
||||
const SHA256 sk = signing_key(digest_factory,
|
||||
creds.secret_key,
|
||||
date.substr(0, 8),
|
||||
region,
|
||||
service);
|
||||
return hmac_sha256(digest_factory, string_to_sign(digest_factory), sk).to_hex();
|
||||
}
|
||||
|
||||
virtual std::string content_hash() const
|
||||
{
|
||||
// SHA256 of empty string
|
||||
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
||||
}
|
||||
|
||||
std::string canonical_request() const
|
||||
{
|
||||
std::string ret =
|
||||
method + '\n'
|
||||
+ uri + '\n'
|
||||
+ parms.canonical_query_string() + '\n'
|
||||
+ "host:" + host + '\n'
|
||||
+ '\n'
|
||||
+ amz_signed_headers() + '\n'
|
||||
+ content_hash();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string amz_signed_headers() const
|
||||
{
|
||||
std::string signed_headers = "host";
|
||||
return signed_headers;
|
||||
}
|
||||
|
||||
std::string string_to_sign(DigestFactory& digest_factory) const
|
||||
{
|
||||
return
|
||||
"AWS4-HMAC-SHA256\n"
|
||||
+ date + '\n'
|
||||
+ amz_credential() + "\n"
|
||||
+ sha256(digest_factory, canonical_request()).to_hex();
|
||||
}
|
||||
|
||||
std::string amz_credential() const
|
||||
{
|
||||
return date.substr(0, 8) + '/' + region + '/' + service + "/aws4_request";
|
||||
}
|
||||
|
||||
virtual ~QueryBuilder() {}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user