0
0
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:
James Yonan 2016-11-09 10:24:58 -08:00 committed by Lev Stipakov
parent 1a4bff66af
commit 702b3a81b7
No known key found for this signature in database
GPG Key ID: 88670BE258B9C258

236
openvpn/aws/awsrest.hpp Normal file
View 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, &lt))
throw aws_rest_error("gmtime_r failed");
if (!::strftime(buf, sizeof(buf),
"%Y%m%dT%H%M%SZ",
&lt))
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