зеркало из https://github.com/microsoft/CCF.git
Support for (request-target) in HTTP client signatures (#625)
This commit is contained in:
Родитель
5624c7d68a
Коммит
3f0edba271
|
@ -86,8 +86,9 @@ public:
|
|||
headers_to_sign.emplace_back(k);
|
||||
}
|
||||
|
||||
const auto signing_string =
|
||||
enclave::construct_raw_signed_string(headers, headers_to_sign);
|
||||
std::string query = "";
|
||||
const auto signing_string = enclave::http::construct_raw_signed_string(
|
||||
http_method_str(HTTP_POST), method, query, headers, headers_to_sign);
|
||||
if (!signing_string.has_value())
|
||||
{
|
||||
throw std::logic_error(fmt::format("Error constructing signed string"));
|
||||
|
|
|
@ -132,7 +132,7 @@ namespace enclave
|
|||
}
|
||||
|
||||
void handle_message(
|
||||
http_method method,
|
||||
http_method verb,
|
||||
const std::string& path,
|
||||
const std::string& query,
|
||||
const http::HeaderMap& headers,
|
||||
|
@ -140,7 +140,7 @@ namespace enclave
|
|||
{
|
||||
LOG_INFO_FMT(
|
||||
"Processing msg({}, {}, {}, [{} bytes])",
|
||||
http_method_str(method),
|
||||
http_method_str(verb),
|
||||
path,
|
||||
query,
|
||||
body.size());
|
||||
|
@ -206,7 +206,8 @@ namespace enclave
|
|||
|
||||
// TODO: For now, set this here as parse_rpc_context() resets
|
||||
// rpc_ctx.signed_request for a HTTP endpoint.
|
||||
auto http_sig_v = HttpSignatureVerifier(headers, body);
|
||||
auto http_sig_v = http::HttpSignatureVerifier(
|
||||
std::string(http_method_str(verb)), path, query, headers, body);
|
||||
auto signed_req = http_sig_v.parse();
|
||||
if (signed_req.has_value())
|
||||
{
|
||||
|
|
|
@ -13,297 +13,335 @@
|
|||
|
||||
namespace enclave
|
||||
{
|
||||
// All HTTP headers are expected to be lowercase
|
||||
static constexpr auto HTTP_HEADER_AUTHORIZATION = "authorization";
|
||||
static constexpr auto HTTP_HEADER_DIGEST = "digest";
|
||||
|
||||
static constexpr auto DIGEST_SHA256 = "SHA-256";
|
||||
|
||||
static constexpr auto AUTH_SCHEME = "Signature";
|
||||
static constexpr auto SIGN_PARAMS_KEYID = "keyId";
|
||||
static constexpr auto SIGN_PARAMS_SIGNATURE = "signature";
|
||||
static constexpr auto SIGN_PARAMS_ALGORITHM = "algorithm";
|
||||
static constexpr auto SIGN_PARAMS_HEADERS = "headers";
|
||||
static constexpr auto SIGN_ALGORITHM = "ecdsa-sha256";
|
||||
|
||||
static constexpr auto SIGN_PARAMS_DELIMITER = ",";
|
||||
static constexpr auto SIGN_PARAMS_HEADERS_DELIMITER = " ";
|
||||
|
||||
std::optional<std::vector<uint8_t>> construct_raw_signed_string(
|
||||
const http::HeaderMap& headers,
|
||||
const std::vector<std::string_view>& headers_to_sign)
|
||||
namespace http
|
||||
{
|
||||
std::string signed_string = {};
|
||||
// All HTTP headers are expected to be lowercase
|
||||
static constexpr auto HTTP_HEADER_AUTHORIZATION = "authorization";
|
||||
static constexpr auto HTTP_HEADER_DIGEST = "digest";
|
||||
|
||||
bool first = true;
|
||||
bool has_digest = false;
|
||||
static constexpr auto DIGEST_SHA256 = "SHA-256";
|
||||
|
||||
for (const auto f : headers_to_sign)
|
||||
static constexpr auto AUTH_SCHEME = "Signature";
|
||||
static constexpr auto SIGN_PARAMS_KEYID = "keyId";
|
||||
static constexpr auto SIGN_PARAMS_SIGNATURE = "signature";
|
||||
static constexpr auto SIGN_PARAMS_ALGORITHM = "algorithm";
|
||||
static constexpr auto SIGN_PARAMS_HEADERS = "headers";
|
||||
static constexpr auto SIGN_ALGORITHM = "ecdsa-sha256";
|
||||
|
||||
static constexpr auto SIGN_HEADER_REQUEST_TARGET = "(request-target)";
|
||||
|
||||
static constexpr auto SIGN_PARAMS_DELIMITER = ",";
|
||||
static constexpr auto SIGN_PARAMS_HEADERS_DELIMITER = " ";
|
||||
|
||||
std::optional<std::vector<uint8_t>> construct_raw_signed_string(
|
||||
std::string verb,
|
||||
const std::string& path,
|
||||
const std::string& query,
|
||||
const http::HeaderMap& headers,
|
||||
const std::vector<std::string_view>& headers_to_sign)
|
||||
{
|
||||
const auto h = headers.find(f);
|
||||
if (h == headers.end())
|
||||
std::string signed_string = {};
|
||||
std::string value = {};
|
||||
bool has_digest = false;
|
||||
bool first = true;
|
||||
|
||||
for (const auto f : headers_to_sign)
|
||||
{
|
||||
LOG_FAIL_FMT("Signed header {} does not exist", f);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Digest field should be signed.
|
||||
if (f == HTTP_HEADER_DIGEST)
|
||||
{
|
||||
has_digest = true;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
{
|
||||
signed_string.append("\n");
|
||||
}
|
||||
first = false;
|
||||
|
||||
signed_string.append(f);
|
||||
signed_string.append(": ");
|
||||
signed_string.append(h->second);
|
||||
}
|
||||
|
||||
if (!has_digest)
|
||||
{
|
||||
LOG_FAIL_FMT("{} is not signed", HTTP_HEADER_DIGEST);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ret =
|
||||
std::vector<uint8_t>({signed_string.begin(), signed_string.end()});
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Implements verification of "Signature" scheme from
|
||||
// https://tools.ietf.org/html/draft-cavage-http-signatures-12
|
||||
//
|
||||
// Tested with RequestClient in tests/infra/clients.py
|
||||
//
|
||||
// TODO:
|
||||
// - Only supports public key crytography (i.e. no HMAC)
|
||||
// - Only supports SHA-256 as digest algorithm
|
||||
// - Only supports ecdsa-sha256 as signature algorithm
|
||||
// - keyId is ignored
|
||||
class HttpSignatureVerifier
|
||||
{
|
||||
private:
|
||||
const http::HeaderMap& headers;
|
||||
const std::vector<uint8_t>& body;
|
||||
|
||||
struct SignatureParams
|
||||
{
|
||||
std::string_view signature = {};
|
||||
std::string_view signature_algorithm = {};
|
||||
std::vector<std::string_view> signed_headers;
|
||||
};
|
||||
|
||||
bool parse_auth_scheme(std::string_view& auth_header_value)
|
||||
{
|
||||
auto next_space = auth_header_value.find(" ");
|
||||
if (next_space == std::string::npos)
|
||||
{
|
||||
LOG_FAIL_FMT("Authorization header only contains one field!");
|
||||
return false;
|
||||
}
|
||||
auto auth_scheme = auth_header_value.substr(0, next_space);
|
||||
if (auth_scheme != AUTH_SCHEME)
|
||||
{
|
||||
LOG_FAIL_FMT("{} is the only supported scheme", AUTH_SCHEME);
|
||||
return false;
|
||||
}
|
||||
auth_header_value = auth_header_value.substr(next_space + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verify_digest()
|
||||
{
|
||||
// First, retrieve digest from header
|
||||
auto digest = headers.find(HTTP_HEADER_DIGEST);
|
||||
if (digest == headers.end())
|
||||
{
|
||||
LOG_FAIL_FMT("HTTP header does not contain {}", HTTP_HEADER_DIGEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto equal_pos = digest->second.find("=");
|
||||
if (equal_pos == std::string::npos)
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"{} header does not contain key=value", HTTP_HEADER_DIGEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sha_key = digest->second.substr(0, equal_pos);
|
||||
if (sha_key != DIGEST_SHA256)
|
||||
{
|
||||
LOG_FAIL_FMT("Only {} digest is supported", DIGEST_SHA256);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto raw_digest = tls::raw_from_b64(digest->second.substr(equal_pos + 1));
|
||||
|
||||
// Then, hash the request body
|
||||
tls::HashBytes body_digest;
|
||||
tls::do_hash(body.data(), body.size(), body_digest, MBEDTLS_MD_SHA256);
|
||||
|
||||
if (raw_digest != body_digest)
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"Request body does not match {} header", HTTP_HEADER_DIGEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses a delimited string with no delimiter at the end
|
||||
// (e.g. "foo,bar,baz") and returns a vector parsed string views (e.g.
|
||||
// ["foo", "bar", "baz"])
|
||||
std::vector<std::string_view> parse_delimited_string(
|
||||
std::string_view& s, const std::string& delimiter)
|
||||
{
|
||||
std::vector<std::string_view> strings;
|
||||
bool last_string = false;
|
||||
|
||||
auto next_delimiter = s.find(delimiter);
|
||||
while (next_delimiter != std::string::npos || !last_string)
|
||||
{
|
||||
auto token = s.substr(0, next_delimiter);
|
||||
if (next_delimiter == std::string::npos)
|
||||
if (f == SIGN_HEADER_REQUEST_TARGET)
|
||||
{
|
||||
last_string = true;
|
||||
}
|
||||
|
||||
strings.emplace_back(token);
|
||||
|
||||
if (!last_string)
|
||||
{
|
||||
s = s.substr(next_delimiter + 1);
|
||||
next_delimiter = s.find(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
std::optional<SignatureParams> parse_signature_params(
|
||||
std::string_view& auth_header_value)
|
||||
{
|
||||
SignatureParams sig_params = {};
|
||||
|
||||
auto parsed_params =
|
||||
parse_delimited_string(auth_header_value, SIGN_PARAMS_DELIMITER);
|
||||
|
||||
for (auto& p : parsed_params)
|
||||
{
|
||||
auto eq_pos = p.find("=");
|
||||
if (eq_pos != std::string::npos)
|
||||
{
|
||||
auto k = p.substr(0, eq_pos);
|
||||
auto v = p.substr(eq_pos + 1);
|
||||
|
||||
// Remove inverted commas around value
|
||||
v.remove_prefix(v.find_first_of("\"") + 1);
|
||||
v.remove_suffix(v.size() - v.find_last_of("\""));
|
||||
|
||||
if (k == SIGN_PARAMS_KEYID)
|
||||
// Store verb as lowercase
|
||||
std::transform(
|
||||
verb.begin(), verb.end(), verb.begin(), [](unsigned char c) {
|
||||
return std::tolower(c);
|
||||
});
|
||||
value = fmt::format("{} {}", verb, path);
|
||||
if (!query.empty())
|
||||
{
|
||||
// keyId is ignored
|
||||
}
|
||||
else if (k == SIGN_PARAMS_ALGORITHM)
|
||||
{
|
||||
sig_params.signature_algorithm = v;
|
||||
if (v != SIGN_ALGORITHM)
|
||||
{
|
||||
LOG_FAIL_FMT("Signature algorithm {} is not supported", v);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (k == SIGN_PARAMS_SIGNATURE)
|
||||
{
|
||||
sig_params.signature = v;
|
||||
}
|
||||
else if (k == SIGN_PARAMS_HEADERS)
|
||||
{
|
||||
auto parsed_signed_headers =
|
||||
parse_delimited_string(v, SIGN_PARAMS_HEADERS_DELIMITER);
|
||||
|
||||
if (parsed_signed_headers.size() == 0)
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"No headers specified in {} field", SIGN_PARAMS_HEADERS);
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto& h : parsed_signed_headers)
|
||||
{
|
||||
sig_params.signed_headers.emplace_back(h);
|
||||
}
|
||||
value.append(fmt::format("?{}", query));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FAIL_FMT("Authorization parameter {} does not contain \"=\"", p);
|
||||
return {};
|
||||
const auto h = headers.find(f);
|
||||
if (h == headers.end())
|
||||
{
|
||||
LOG_FAIL_FMT("Signed header {} does not exist", f);
|
||||
return {};
|
||||
}
|
||||
|
||||
value = h->second;
|
||||
|
||||
// Digest field should be signed.
|
||||
if (f == HTTP_HEADER_DIGEST)
|
||||
{
|
||||
has_digest = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!first)
|
||||
{
|
||||
signed_string.append("\n");
|
||||
}
|
||||
first = false;
|
||||
|
||||
signed_string.append(f);
|
||||
signed_string.append(": ");
|
||||
signed_string.append(value);
|
||||
}
|
||||
|
||||
return sig_params;
|
||||
}
|
||||
|
||||
public:
|
||||
HttpSignatureVerifier(
|
||||
const http::HeaderMap& headers_, const std::vector<uint8_t>& body_) :
|
||||
headers(headers_),
|
||||
body(body_)
|
||||
{}
|
||||
|
||||
std::optional<ccf::SignedReq> parse()
|
||||
{
|
||||
auto auth = headers.find(HTTP_HEADER_AUTHORIZATION);
|
||||
if (auth != headers.end())
|
||||
if (!has_digest)
|
||||
{
|
||||
std::string_view authz_header = auth->second;
|
||||
|
||||
if (!parse_auth_scheme(authz_header))
|
||||
{
|
||||
throw std::logic_error(fmt::format(
|
||||
"Error parsing {} scheme. Only {} is supported",
|
||||
HTTP_HEADER_AUTHORIZATION,
|
||||
AUTH_SCHEME));
|
||||
}
|
||||
|
||||
if (!verify_digest())
|
||||
{
|
||||
throw std::logic_error(
|
||||
fmt::format("Error verifying HTTP {} header", HTTP_HEADER_DIGEST));
|
||||
}
|
||||
|
||||
auto parsed_sign_params = parse_signature_params(authz_header);
|
||||
if (!parsed_sign_params.has_value())
|
||||
{
|
||||
throw std::logic_error(
|
||||
fmt::format("Error parsing {} fields", HTTP_HEADER_AUTHORIZATION));
|
||||
}
|
||||
|
||||
auto signed_raw = construct_raw_signed_string(
|
||||
headers, parsed_sign_params->signed_headers);
|
||||
if (!signed_raw.has_value())
|
||||
{
|
||||
throw std::logic_error(
|
||||
fmt::format("Error constructing signed string"));
|
||||
}
|
||||
|
||||
auto sig_raw = tls::raw_from_b64(parsed_sign_params->signature);
|
||||
auto raw_req = std::vector<uint8_t>({body.begin(), body.end()});
|
||||
ccf::SignedReq ret = {
|
||||
sig_raw, signed_raw.value(), raw_req, MBEDTLS_MD_SHA256};
|
||||
return ret;
|
||||
LOG_FAIL_FMT("{} is not signed", HTTP_HEADER_DIGEST);
|
||||
return {};
|
||||
}
|
||||
|
||||
// The request does not contain the Authorization header
|
||||
return {};
|
||||
auto ret =
|
||||
std::vector<uint8_t>({signed_string.begin(), signed_string.end()});
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
// Implements verification of "Signature" scheme from
|
||||
// https://tools.ietf.org/html/draft-cavage-http-signatures-12
|
||||
//
|
||||
// Tested with RequestClient in tests/infra/clients.py
|
||||
//
|
||||
// TODO:
|
||||
// - Only supports public key crytography (i.e. no HMAC)
|
||||
// - Only supports SHA-256 as digest algorithm
|
||||
// - Only supports ecdsa-sha256 as signature algorithm
|
||||
// - keyId is ignored
|
||||
class HttpSignatureVerifier
|
||||
{
|
||||
private:
|
||||
const std::string& verb;
|
||||
const std::string& path;
|
||||
const std::string& query;
|
||||
const http::HeaderMap& headers;
|
||||
const std::vector<uint8_t>& body;
|
||||
|
||||
struct SignatureParams
|
||||
{
|
||||
std::string_view signature = {};
|
||||
std::string_view signature_algorithm = {};
|
||||
std::vector<std::string_view> signed_headers;
|
||||
};
|
||||
|
||||
bool parse_auth_scheme(std::string_view& auth_header_value)
|
||||
{
|
||||
auto next_space = auth_header_value.find(" ");
|
||||
if (next_space == std::string::npos)
|
||||
{
|
||||
LOG_FAIL_FMT("Authorization header only contains one field!");
|
||||
return false;
|
||||
}
|
||||
auto auth_scheme = auth_header_value.substr(0, next_space);
|
||||
if (auth_scheme != AUTH_SCHEME)
|
||||
{
|
||||
LOG_FAIL_FMT("{} is the only supported scheme", AUTH_SCHEME);
|
||||
return false;
|
||||
}
|
||||
auth_header_value = auth_header_value.substr(next_space + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verify_digest()
|
||||
{
|
||||
// First, retrieve digest from header
|
||||
auto digest = headers.find(HTTP_HEADER_DIGEST);
|
||||
if (digest == headers.end())
|
||||
{
|
||||
LOG_FAIL_FMT("HTTP header does not contain {}", HTTP_HEADER_DIGEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto equal_pos = digest->second.find("=");
|
||||
if (equal_pos == std::string::npos)
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"{} header does not contain key=value", HTTP_HEADER_DIGEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sha_key = digest->second.substr(0, equal_pos);
|
||||
if (sha_key != DIGEST_SHA256)
|
||||
{
|
||||
LOG_FAIL_FMT("Only {} digest is supported", DIGEST_SHA256);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto raw_digest =
|
||||
tls::raw_from_b64(digest->second.substr(equal_pos + 1));
|
||||
|
||||
// Then, hash the request body
|
||||
tls::HashBytes body_digest;
|
||||
tls::do_hash(body.data(), body.size(), body_digest, MBEDTLS_MD_SHA256);
|
||||
|
||||
if (raw_digest != body_digest)
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"Request body does not match {} header", HTTP_HEADER_DIGEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses a delimited string with no delimiter at the end
|
||||
// (e.g. "foo,bar,baz") and returns a vector parsed string views (e.g.
|
||||
// ["foo", "bar", "baz"])
|
||||
std::vector<std::string_view> parse_delimited_string(
|
||||
std::string_view& s, const std::string& delimiter)
|
||||
{
|
||||
std::vector<std::string_view> strings;
|
||||
bool last_string = false;
|
||||
|
||||
auto next_delimiter = s.find(delimiter);
|
||||
while (next_delimiter != std::string::npos || !last_string)
|
||||
{
|
||||
auto token = s.substr(0, next_delimiter);
|
||||
if (next_delimiter == std::string::npos)
|
||||
{
|
||||
last_string = true;
|
||||
}
|
||||
|
||||
strings.emplace_back(token);
|
||||
|
||||
if (!last_string)
|
||||
{
|
||||
s = s.substr(next_delimiter + 1);
|
||||
next_delimiter = s.find(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
std::optional<SignatureParams> parse_signature_params(
|
||||
std::string_view& auth_header_value)
|
||||
{
|
||||
SignatureParams sig_params = {};
|
||||
|
||||
auto parsed_params =
|
||||
parse_delimited_string(auth_header_value, SIGN_PARAMS_DELIMITER);
|
||||
|
||||
for (auto& p : parsed_params)
|
||||
{
|
||||
auto eq_pos = p.find("=");
|
||||
if (eq_pos != std::string::npos)
|
||||
{
|
||||
auto k = p.substr(0, eq_pos);
|
||||
auto v = p.substr(eq_pos + 1);
|
||||
|
||||
// Remove inverted commas around value
|
||||
v.remove_prefix(v.find_first_of("\"") + 1);
|
||||
v.remove_suffix(v.size() - v.find_last_of("\""));
|
||||
|
||||
if (k == SIGN_PARAMS_KEYID)
|
||||
{
|
||||
// keyId is ignored
|
||||
}
|
||||
else if (k == SIGN_PARAMS_ALGORITHM)
|
||||
{
|
||||
sig_params.signature_algorithm = v;
|
||||
if (v != SIGN_ALGORITHM)
|
||||
{
|
||||
LOG_FAIL_FMT("Signature algorithm {} is not supported", v);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (k == SIGN_PARAMS_SIGNATURE)
|
||||
{
|
||||
sig_params.signature = v;
|
||||
}
|
||||
else if (k == SIGN_PARAMS_HEADERS)
|
||||
{
|
||||
auto parsed_signed_headers =
|
||||
parse_delimited_string(v, SIGN_PARAMS_HEADERS_DELIMITER);
|
||||
|
||||
if (parsed_signed_headers.size() == 0)
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"No headers specified in {} field", SIGN_PARAMS_HEADERS);
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto& h : parsed_signed_headers)
|
||||
{
|
||||
sig_params.signed_headers.emplace_back(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FAIL_FMT(
|
||||
"Authorization parameter {} does not contain \"=\"", p);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return sig_params;
|
||||
}
|
||||
|
||||
public:
|
||||
HttpSignatureVerifier(
|
||||
const std::string& verb_,
|
||||
const std::string& path_,
|
||||
const std::string& query_,
|
||||
const http::HeaderMap& headers_,
|
||||
const std::vector<uint8_t>& body_) :
|
||||
verb(verb_),
|
||||
path(path_),
|
||||
query(query_),
|
||||
headers(headers_),
|
||||
body(body_)
|
||||
{}
|
||||
|
||||
std::optional<ccf::SignedReq> parse()
|
||||
{
|
||||
auto auth = headers.find(HTTP_HEADER_AUTHORIZATION);
|
||||
if (auth != headers.end())
|
||||
{
|
||||
std::string_view authz_header = auth->second;
|
||||
|
||||
if (!parse_auth_scheme(authz_header))
|
||||
{
|
||||
throw std::logic_error(fmt::format(
|
||||
"Error parsing {} scheme. Only {} is supported",
|
||||
HTTP_HEADER_AUTHORIZATION,
|
||||
AUTH_SCHEME));
|
||||
}
|
||||
|
||||
if (!verify_digest())
|
||||
{
|
||||
throw std::logic_error(fmt::format(
|
||||
"Error verifying HTTP {} header", HTTP_HEADER_DIGEST));
|
||||
}
|
||||
|
||||
auto parsed_sign_params = parse_signature_params(authz_header);
|
||||
if (!parsed_sign_params.has_value())
|
||||
{
|
||||
throw std::logic_error(fmt::format(
|
||||
"Error parsing {} fields", HTTP_HEADER_AUTHORIZATION));
|
||||
}
|
||||
|
||||
auto signed_raw = construct_raw_signed_string(
|
||||
verb, path, query, headers, parsed_sign_params->signed_headers);
|
||||
if (!signed_raw.has_value())
|
||||
{
|
||||
throw std::logic_error(
|
||||
fmt::format("Error constructing signed string"));
|
||||
}
|
||||
|
||||
auto sig_raw = tls::raw_from_b64(parsed_sign_params->signature);
|
||||
auto raw_req = std::vector<uint8_t>({body.begin(), body.end()});
|
||||
ccf::SignedReq ret = {
|
||||
sig_raw, signed_raw.value(), raw_req, MBEDTLS_MD_SHA256};
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The request does not contain the Authorization header
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -379,7 +379,7 @@ class RequestClient:
|
|||
def request(self, request):
|
||||
rep = requests.post(
|
||||
f"https://{self.host}:{self.port}/{request.method}",
|
||||
json=request.to_dict(), # TODO: For REST queries, use data= instead
|
||||
json=request.to_dict(),
|
||||
cert=(self.cert, self.key),
|
||||
verify=self.ca,
|
||||
timeout=self.request_timeout,
|
||||
|
@ -391,13 +391,16 @@ class RequestClient:
|
|||
with open(self.key, "rb") as k:
|
||||
rep = requests.post(
|
||||
f"https://{self.host}:{self.port}/{request.method}",
|
||||
json=request.to_dict(), # TODO: For REST queries, use data= instead
|
||||
json=request.to_dict(),
|
||||
cert=(self.cert, self.key),
|
||||
verify=self.ca,
|
||||
timeout=self.request_timeout,
|
||||
# key_id needs to be specified but is unused
|
||||
auth=HTTPSignatureAuth(
|
||||
algorithm="ecdsa-sha256", key=k.read(), key_id="tls",
|
||||
algorithm="ecdsa-sha256",
|
||||
key=k.read(),
|
||||
key_id="tls",
|
||||
headers=["(request-target)", "Date"],
|
||||
),
|
||||
)
|
||||
self.stream.update(rep.content)
|
||||
|
|
Загрузка…
Ссылка в новой задаче