| // 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<const uint8_t> input) { |
| std::string output; |
| base::Base64UrlEncode(input, base::Base64UrlEncodePolicy::OMIT_PADDING, |
| &output); |
| return output; |
| } |
| |
| bssl::UniquePtr<EVP_PKEY> ParsePublicKey(base::span<const uint8_t> pkey_spki) { |
| CBS cbs; |
| CBS_init(&cbs, pkey_spki.data(), pkey_spki.size()); |
| bssl::UniquePtr<EVP_PKEY> pkey(EVP_parse_public_key(&cbs)); |
| if (CBS_len(&cbs) != 0) { |
| return nullptr; |
| } |
| return pkey; |
| } |
| |
| base::Value::Dict ConvertES256PkeySpkiToJwk( |
| base::span<const uint8_t> pkey_spki) { |
| bssl::UniquePtr<EVP_PKEY> 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<BIGNUM> x(BN_new()); |
| bssl::UniquePtr<BIGNUM> 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<uint8_t> x_bytes(32); |
| std::vector<uint8_t> y_bytes(32); |
| if (!BN_bn2bin_padded(x_bytes.data(), x_bytes.size(), x.get()) || |
| !BN_bn2bin_padded(y_bytes.data(), y_bytes.size(), y.get())) { |
| return base::Value::Dict(); |
| } |
| |
| 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<const uint8_t> pkey_spki) { |
| bssl::UniquePtr<EVP_PKEY> 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<uint8_t> n_bytes(BN_num_bytes(n)); |
| std::vector<uint8_t> 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<const uint8_t> 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 |