Support for hs2019 in HTTP signature scheme (#1872)

This commit is contained in:
Julien Maffre 2020-11-10 17:02:33 +00:00 коммит произвёл GitHub
Родитель 7afef2cc2b
Коммит 98eaff2543
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 42 добавлений и 32 удалений

Просмотреть файл

@ -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 = (