зеркало из https://github.com/microsoft/CCF.git
Support for hs2019 in HTTP signature scheme (#1872)
This commit is contained in:
Родитель
7afef2cc2b
Коммит
98eaff2543
|
@ -79,11 +79,14 @@ string_to_sign="(request-target): ${command,,} ${url}
|
|||
digest: SHA-256=$req_digest
|
||||
content-length: $content_length"
|
||||
|
||||
# https://tools.ietf.org/html/draft-cavage-http-signatures-12#appendix-E.2
|
||||
signature_algorithm="hs2019"
|
||||
|
||||
# Compute signature
|
||||
signed_raw=$(echo -n "$string_to_sign" | openssl dgst -sha256 -sign "$privk" | openssl base64 -A)
|
||||
signed_raw=$(echo -n "$string_to_sign" | openssl dgst -sha384 -sign "$privk" | openssl base64 -A)
|
||||
|
||||
curl \
|
||||
-H "Digest: SHA-256=$req_digest" \
|
||||
-H "Authorization: Signature keyId=\"tls\",algorithm=\"ecdsa-sha256\",headers=\"(request-target) digest content-length\",signature=\"$signed_raw\"" \
|
||||
-H "Authorization: Signature keyId=\"tls\",signature_algorithm=\"$signature_algorithm\",headers=\"(request-target) digest content-length\",signature=\"$signed_raw\"" \
|
||||
"${additional_curl_args[@]}" \
|
||||
"$@"
|
|
@ -40,12 +40,13 @@ namespace http
|
|||
{
|
||||
static constexpr auto DIGEST_SHA256 = "SHA-256";
|
||||
|
||||
static constexpr auto AUTH_SCHEME = "Signature";
|
||||
static constexpr auto SIGN_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_SHA256 = "ecdsa-sha256";
|
||||
static constexpr auto SIGN_ALGORITHM_ECDSA_SHA256 = "ecdsa-sha256";
|
||||
static constexpr auto SIGN_ALGORITHM_HS_2019 = "hs2019";
|
||||
|
||||
static constexpr auto SIGN_HEADER_REQUEST_TARGET = "(request-target)";
|
||||
|
||||
|
|
|
@ -110,13 +110,13 @@ namespace http
|
|||
throw std::logic_error("Unable to sign HTTP request");
|
||||
}
|
||||
|
||||
const auto signature = kp->sign(to_sign.value(), MBEDTLS_MD_SHA256);
|
||||
const auto signature = kp->sign(to_sign.value());
|
||||
|
||||
auto auth_value = fmt::format(
|
||||
"Signature "
|
||||
"keyId=\"ignored\",algorithm=\"{}\",headers=\"{}\",signature="
|
||||
"\"{}\"",
|
||||
auth::SIGN_ALGORITHM_SHA256,
|
||||
auth::SIGN_ALGORITHM_HS_2019,
|
||||
fmt::format("{}", fmt::join(headers_to_sign, " ")),
|
||||
tls::b64_from_raw(signature.data(), signature.size()));
|
||||
|
||||
|
@ -145,12 +145,10 @@ namespace http
|
|||
// Implements verification of "Signature" scheme from
|
||||
// https://tools.ietf.org/html/draft-cavage-http-signatures-12
|
||||
//
|
||||
// Tested with RequestClient in tests/infra/clients.py
|
||||
//
|
||||
// Notes:
|
||||
// - Only supports public key crytography (i.e. no HMAC)
|
||||
// - Only supports SHA-256 as digest algorithm
|
||||
// - Only supports ecdsa-sha256 as signature algorithm
|
||||
// - Only supports SHA-256 as request digest algorithm
|
||||
// - Only supports ecdsa-sha256 and hs2019 as signature algorithms
|
||||
// - keyId is ignored
|
||||
class HttpSignatureVerifier
|
||||
{
|
||||
|
@ -167,11 +165,11 @@ namespace http
|
|||
auto next_space = auth_header_value.find(" ");
|
||||
if (next_space == std::string::npos)
|
||||
{
|
||||
LOG_FAIL_FMT("Authorization header only contains one field!");
|
||||
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::AUTH_SCHEME)
|
||||
if (auth_scheme != auth::SIGN_AUTH_SCHEME)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -203,8 +201,8 @@ namespace http
|
|||
auto sha_key = digest->second.substr(0, equal_pos);
|
||||
if (sha_key != auth::DIGEST_SHA256)
|
||||
{
|
||||
error_reason =
|
||||
fmt::format("Only {} digest is supported", auth::DIGEST_SHA256);
|
||||
error_reason = fmt::format(
|
||||
"Only {} for request digest is supported", auth::DIGEST_SHA256);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -294,7 +292,9 @@ namespace http
|
|||
else if (k == auth::SIGN_PARAMS_ALGORITHM)
|
||||
{
|
||||
sig_params.signature_algorithm = v;
|
||||
if (v != auth::SIGN_ALGORITHM_SHA256)
|
||||
if (
|
||||
v != auth::SIGN_ALGORITHM_ECDSA_SHA256 &&
|
||||
v != auth::SIGN_ALGORITHM_HS_2019)
|
||||
{
|
||||
LOG_FAIL_FMT("Signature algorithm {} is not supported", v);
|
||||
return std::nullopt;
|
||||
|
@ -394,8 +394,17 @@ namespace http
|
|||
}
|
||||
|
||||
auto sig_raw = tls::raw_from_b64(parsed_sign_params->signature);
|
||||
|
||||
mbedtls_md_type_t signature_digest = MBEDTLS_MD_NONE;
|
||||
if (
|
||||
parsed_sign_params->signature_algorithm ==
|
||||
auth::SIGN_ALGORITHM_ECDSA_SHA256)
|
||||
{
|
||||
signature_digest = MBEDTLS_MD_SHA256;
|
||||
}
|
||||
|
||||
ccf::SignedReq ret = {
|
||||
sig_raw, signed_raw.value(), body, MBEDTLS_MD_SHA256};
|
||||
sig_raw, signed_raw.value(), body, signature_digest};
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,13 +26,13 @@ namespace ccf
|
|||
{
|
||||
// signature
|
||||
std::vector<uint8_t> sig = {};
|
||||
// the signed content
|
||||
// signed content
|
||||
std::vector<uint8_t> req = {};
|
||||
|
||||
// the request body
|
||||
// request body
|
||||
std::vector<uint8_t> request_body = {};
|
||||
|
||||
// the hashing algorithm used
|
||||
// signature hashing algorithm used
|
||||
mbedtls_md_type_t md = MBEDTLS_MD_NONE;
|
||||
|
||||
bool operator==(const SignedReq& other) const
|
||||
|
|
|
@ -378,8 +378,7 @@ std::pair<http::Request, ccf::SignedReq> create_signed_request(
|
|||
|
||||
ccf::SignedReq signed_req{details.signature,
|
||||
details.to_sign,
|
||||
body == nullptr ? std::vector<uint8_t>() : *body,
|
||||
MBEDTLS_MD_SHA256};
|
||||
body == nullptr ? std::vector<uint8_t>() : *body};
|
||||
return {s, signed_req};
|
||||
}
|
||||
|
||||
|
|
|
@ -102,14 +102,8 @@ def verify_request_sig(raw_cert, sig, req, request_body, md):
|
|||
try:
|
||||
cert = x509.load_der_x509_certificate(raw_cert, backend=default_backend())
|
||||
|
||||
digest = (
|
||||
hashes.SHA256()
|
||||
if md == CCFDigestType.MD_SHA256
|
||||
else cert.signature_hash_algorithm
|
||||
)
|
||||
|
||||
# verify that the digest matches the hash of the body
|
||||
h = hashes.Hash(digest, backend=default_backend())
|
||||
# Verify that the request digest matches the hash of the body
|
||||
h = hashes.Hash(hashes.SHA256(), backend=default_backend())
|
||||
h.update(request_body)
|
||||
raw_req_digest = h.finalize()
|
||||
header_digest = base64.b64decode(req.decode().split("SHA-256=")[1])
|
||||
|
@ -118,8 +112,12 @@ def verify_request_sig(raw_cert, sig, req, request_body, md):
|
|||
), "Digest header does not match request body"
|
||||
|
||||
pub_key = cert.public_key()
|
||||
hash_alg = ec.ECDSA(digest)
|
||||
pub_key.verify(sig, req, hash_alg)
|
||||
signature_hash_alg = ec.ECDSA(
|
||||
hashes.SHA256()
|
||||
if md == CCFDigestType.MD_SHA256
|
||||
else cert.signature_hash_algorithm
|
||||
)
|
||||
pub_key.verify(sig, req, signature_hash_alg)
|
||||
except InvalidSignature as e:
|
||||
# we support a non-standard curve, which is also being
|
||||
# used for bitcoin.
|
||||
|
@ -154,7 +152,7 @@ def generate_cert(priv_key_pem: str) -> str:
|
|||
pub = priv.public_key()
|
||||
subject = issuer = x509.Name(
|
||||
[
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, u"dummy"),
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, "dummy"),
|
||||
]
|
||||
)
|
||||
cert = (
|
||||
|
|
Загрузка…
Ссылка в новой задаче