// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/device_bound_sessions/jwk_utils.h" #include "base/base64url.h" #include "third_party/boringssl/src/include/openssl/bn.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/ec.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/rsa.h" namespace net::device_bound_sessions { namespace { // The format of JSON Web Key (JWK) is specified in the section 4 of RFC 7517: // https://www.ietf.org/rfc/rfc7517.html#section-4 // // The parameters of a particular key type are specified by the JWA spec: // https://www.ietf.org/rfc/rfc7518.html#section-6 constexpr char kKeyTypeParam[] = "kty"; constexpr char kEcKeyType[] = "EC"; constexpr char kEcCurve[] = "crv"; constexpr char kEcCurveP256[] = "P-256"; constexpr char kEcCoordinateX[] = "x"; constexpr char kEcCoordinateY[] = "y"; constexpr char kRsaKeyType[] = "RSA"; constexpr char kRsaModulus[] = "n"; constexpr char kRsaExponent[] = "e"; std::string Base64UrlEncode(base::span input) { std::string output; base::Base64UrlEncode(input, base::Base64UrlEncodePolicy::OMIT_PADDING, &output); return output; } bssl::UniquePtr ParsePublicKey(base::span pkey_spki) { CBS cbs; CBS_init(&cbs, pkey_spki.data(), pkey_spki.size()); bssl::UniquePtr pkey(EVP_parse_public_key(&cbs)); if (CBS_len(&cbs) != 0) { return nullptr; } return pkey; } base::Value::Dict ConvertES256PkeySpkiToJwk( base::span pkey_spki) { bssl::UniquePtr pkey = ParsePublicKey(pkey_spki); if (!pkey || EVP_PKEY_id(pkey.get()) != EVP_PKEY_EC) { return base::Value::Dict(); } EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get()); if (!ec_key) { return base::Value::Dict(); } const EC_GROUP* group = EC_KEY_get0_group(ec_key); const EC_POINT* point = EC_KEY_get0_public_key(ec_key); if (!group || !point) { return base::Value::Dict(); } bssl::UniquePtr x(BN_new()); bssl::UniquePtr y(BN_new()); if (!x || !y) { return base::Value::Dict(); } if (!EC_POINT_get_affine_coordinates_GFp(group, point, x.get(), y.get(), nullptr)) { return base::Value::Dict(); } std::vector x_bytes(BN_num_bytes(x.get())); std::vector y_bytes(BN_num_bytes(y.get())); BN_bn2bin(x.get(), x_bytes.data()); BN_bn2bin(y.get(), y_bytes.data()); return base::Value::Dict() .Set(kKeyTypeParam, kEcKeyType) .Set(kEcCurve, kEcCurveP256) .Set(kEcCoordinateX, Base64UrlEncode(x_bytes)) .Set(kEcCoordinateY, Base64UrlEncode(y_bytes)); } base::Value::Dict ConvertRS256PkeySpkiToJwk( base::span pkey_spki) { bssl::UniquePtr pkey = ParsePublicKey(pkey_spki); if (!pkey || EVP_PKEY_id(pkey.get()) != EVP_PKEY_RSA) { return base::Value::Dict(); } RSA* rsa_key = EVP_PKEY_get0_RSA(pkey.get()); if (!rsa_key) { return base::Value::Dict(); } const BIGNUM* n = RSA_get0_n(rsa_key); const BIGNUM* e = RSA_get0_e(rsa_key); if (!n || !e) { return base::Value::Dict(); } std::vector n_bytes(BN_num_bytes(n)); std::vector e_bytes(BN_num_bytes(e)); BN_bn2bin(n, n_bytes.data()); BN_bn2bin(e, e_bytes.data()); return base::Value::Dict() .Set(kKeyTypeParam, kRsaKeyType) .Set(kRsaModulus, Base64UrlEncode(n_bytes)) .Set(kRsaExponent, Base64UrlEncode(e_bytes)); } } // namespace base::Value::Dict ConvertPkeySpkiToJwk( crypto::SignatureVerifier::SignatureAlgorithm algorithm, base::span pkey_spki) { // TODO(crbug.com/360756896): Support more algorithms. switch (algorithm) { case crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256: return ConvertRS256PkeySpkiToJwk(pkey_spki); case crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256: return ConvertES256PkeySpkiToJwk(pkey_spki); default: return base::Value::Dict(); } } } // namespace net::device_bound_sessions