OpenVPN 3 Core Library
Loading...
Searching...
No Matches
awsrest.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// AWS REST API query utilities such as query signing
13
14#pragma once
15
16#include <string>
17#include <vector>
18#include <cstdint> // for std::uint8_t
19#include <algorithm>
20#include <utility>
21#include <time.h>
22
28
29namespace openvpn::AWS {
30class REST
31{
32 public:
33 OPENVPN_EXCEPTION(aws_rest_error);
34
35 // 20130524T000000Z
36 static std::string amz_date()
37 {
38 struct tm lt;
39 char buf[64];
40 const time_t t = ::time(nullptr);
41 if (!::gmtime_r(&t, &lt))
42 throw aws_rest_error("gmtime_r failed");
43 if (!::strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", &lt))
44 throw aws_rest_error("strftime failed");
45 return std::string(buf);
46 }
47
48 struct SHA256
49 {
50 std::string to_hex() const
51 {
52 return render_hex(hash, sizeof(hash));
53 }
54
55 std::uint8_t hash[32];
56 };
57
58 static SHA256 hmac_sha256(DigestFactory &digest_factory, const std::string &data, const std::string &key)
59 {
60 SHA256 ret;
61 HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, (const std::uint8_t *)key.c_str(), key.length()));
62 hi->update((const std::uint8_t *)data.c_str(), data.length());
63 hi->final(ret.hash);
64 return ret;
65 }
66
67 static SHA256 hmac_sha256(DigestFactory &digest_factory, const std::string &data, const SHA256 &key)
68 {
69 SHA256 ret;
70 HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, key.hash, sizeof(key.hash)));
71 hi->update((const std::uint8_t *)data.c_str(), data.length());
72 hi->final(ret.hash);
73 return ret;
74 }
75
76 static SHA256 sha256(DigestFactory &digest_factory, const std::string &data)
77 {
78 SHA256 ret;
80 di->update((const std::uint8_t *)data.c_str(), data.length());
81 di->final(ret.hash);
82 return ret;
83 }
84
86 const std::string &key,
87 const std::string &date_stamp,
88 const std::string &region_name,
89 const std::string &service_name)
90 {
91 const SHA256 h1 = hmac_sha256(df, date_stamp, "AWS4" + key);
92 const SHA256 h2 = hmac_sha256(df, region_name, h1);
93 const SHA256 h3 = hmac_sha256(df, service_name, h2);
94 const SHA256 h4 = hmac_sha256(df, "aws4_request", h3);
95 return h4;
96 }
97
98 struct KeyValue
99 {
100 KeyValue(std::string key_arg, std::string value_arg)
101 : key(std::move(key_arg)),
102 value(std::move(value_arg))
103 {
104 }
105
106 bool operator<(const KeyValue &rhs) const
107 {
108 return key < rhs.key;
109 }
110
111 std::string uri_encode() const
112 {
113 return URL::encode(key) + '=' + URL::encode(value);
114 }
115
116 std::string key;
117 std::string value;
118 };
119
120 struct Query : public std::vector<KeyValue>
121 {
122 std::string canonical_query_string() const
123 {
124 bool first = true;
125 std::string ret;
126 for (auto &p : *this)
127 {
128 if (!first)
129 ret += '&';
130 ret += p.uri_encode();
131 first = false;
132 }
133 return ret;
134 }
135
136 void sort()
137 {
138 std::sort(begin(), end());
139 }
140 };
141
143 {
144 std::string date; // such as "20130524T000000Z"
145 unsigned int expires = 300; // request expiration in seconds
146 std::string region; // such as "us-east-1"
147 std::string service; // such as "s3"
148 std::string method; // such as "GET"
149 std::string host; // such as "ec2.us-west-2.amazonaws.com"
150 std::string uri; // such as "/"
152
153 std::string uri_query() const
154 {
155 return uri + '?' + parms.canonical_query_string();
156 }
157
158 std::string url_query() const
159 {
160 return "https://" + host + uri_query();
161 }
162
163 void add_amz_parms(const Creds &creds)
164 {
165 parms.emplace_back("X-Amz-Algorithm", "AWS4-HMAC-SHA256");
166 parms.emplace_back("X-Amz-Credential", creds.access_key + '/' + amz_credential());
167 parms.emplace_back("X-Amz-Date", date);
168 parms.emplace_back("X-Amz-Expires", std::to_string(expires));
169 parms.emplace_back("X-Amz-SignedHeaders", amz_signed_headers());
170
171 if (!creds.token.empty())
172 parms.emplace_back("X-Amz-Security-Token", creds.token);
173 }
174
176 {
177 parms.sort();
178 }
179
180 void add_amz_signature(DigestFactory &digest_factory, const Creds &creds)
181 {
182 parms.emplace_back("X-Amz-Signature", signature(digest_factory, creds));
183 }
184
185 std::string signature(DigestFactory &digest_factory, const Creds &creds) const
186 {
187 const SHA256 sk = signing_key(digest_factory,
188 creds.secret_key,
189 date.substr(0, 8),
190 region,
191 service);
192 return hmac_sha256(digest_factory, string_to_sign(digest_factory), sk).to_hex();
193 }
194
195 virtual std::string content_hash() const
196 {
197 // SHA256 of empty string
198 return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
199 }
200
201 std::string canonical_request() const
202 {
203 std::string ret = method + '\n'
204 + uri + '\n'
206 + "host:" + host + '\n'
207 + '\n'
208 + amz_signed_headers() + '\n';
209 if (service == "s3")
210 ret += "UNSIGNED-PAYLOAD";
211 else
212 ret += content_hash();
213 return ret;
214 }
215
216 std::string amz_signed_headers() const
217 {
218 std::string signed_headers = "host";
219 return signed_headers;
220 }
221
222 std::string string_to_sign(DigestFactory &digest_factory) const
223 {
224 return "AWS4-HMAC-SHA256\n"
225 + date + '\n'
226 + amz_credential() + "\n"
227 + sha256(digest_factory, canonical_request()).to_hex();
228 }
229
230 std::string amz_credential() const
231 {
232 return date.substr(0, 8) + '/' + region + '/' + service + "/aws4_request";
233 }
234
235 virtual ~QueryBuilder() = default;
236 };
237};
238} // namespace openvpn::AWS
static SHA256 hmac_sha256(DigestFactory &digest_factory, const std::string &data, const std::string &key)
Definition awsrest.hpp:58
OPENVPN_EXCEPTION(aws_rest_error)
static SHA256 sha256(DigestFactory &digest_factory, const std::string &data)
Definition awsrest.hpp:76
static SHA256 hmac_sha256(DigestFactory &digest_factory, const std::string &data, const SHA256 &key)
Definition awsrest.hpp:67
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)
Definition awsrest.hpp:85
static std::string amz_date()
Definition awsrest.hpp:36
virtual HMACInstance::Ptr new_hmac(const CryptoAlgs::Type digest_type, const unsigned char *key, const size_t key_size)=0
virtual DigestInstance::Ptr new_digest(const CryptoAlgs::Type digest_type)=0
virtual size_t final(unsigned char *out)=0
virtual void update(const unsigned char *in, const size_t size)=0
The smart pointer class.
Definition rc.hpp:119
std::string encode(const std::string &str)
Definition urlencode.hpp:28
std::string render_hex(const unsigned char *data, size_t size, const bool caps=false)
Definition hexstr.hpp:135
std::string access_key
Definition awscreds.hpp:52
std::string token
Definition awscreds.hpp:54
std::string secret_key
Definition awscreds.hpp:53
bool operator<(const KeyValue &rhs) const
Definition awsrest.hpp:106
std::string uri_encode() const
Definition awsrest.hpp:111
KeyValue(std::string key_arg, std::string value_arg)
Definition awsrest.hpp:100
void add_amz_parms(const Creds &creds)
Definition awsrest.hpp:163
std::string url_query() const
Definition awsrest.hpp:158
std::string canonical_request() const
Definition awsrest.hpp:201
std::string amz_credential() const
Definition awsrest.hpp:230
std::string uri_query() const
Definition awsrest.hpp:153
virtual std::string content_hash() const
Definition awsrest.hpp:195
std::string amz_signed_headers() const
Definition awsrest.hpp:216
std::string signature(DigestFactory &digest_factory, const Creds &creds) const
Definition awsrest.hpp:185
std::string string_to_sign(DigestFactory &digest_factory) const
Definition awsrest.hpp:222
void add_amz_signature(DigestFactory &digest_factory, const Creds &creds)
Definition awsrest.hpp:180
std::string canonical_query_string() const
Definition awsrest.hpp:122
std::uint8_t hash[32]
Definition awsrest.hpp:55
std::string to_hex() const
Definition awsrest.hpp:50
std::string ret