| // Copyright 2018 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| #include "tink/subtle/pem_parser_boringssl.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "openssl/bio.h" |
| #include "openssl/bn.h" |
| #include "openssl/ec.h" |
| #include "openssl/evp.h" |
| #include "openssl/pem.h" |
| #include "openssl/rsa.h" |
| #include "tink/internal/bn_util.h" |
| #include "tink/internal/ec_util.h" |
| #include "tink/internal/rsa_util.h" |
| #include "tink/internal/ssl_unique_ptr.h" |
| #include "tink/internal/ssl_util.h" |
| #include "tink/subtle/common_enums.h" |
| #include "tink/subtle/subtle_util_boringssl.h" |
| #include "tink/util/status.h" |
| #include "tink/util/statusor.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| |
| namespace { |
| constexpr int kBsslOk = 1; |
| |
| // Verifies that the given RSA pointer `rsa_key` points to a valid RSA key. |
| util::Status VerifyRsaKey(const RSA* rsa_key) { |
| if (rsa_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Invalid RSA key format"); |
| } |
| // Check the key parameters. |
| if (RSA_check_key(rsa_key) != kBsslOk) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Invalid RSA key format"); |
| } |
| return util::OkStatus(); |
| } |
| |
| // Verifies that the given ECDSA pointer `ecdsa_key` points to a valid ECDSA |
| // key. |
| util::Status VerifyEcdsaKey(const EC_KEY* ecdsa_key) { |
| if (ecdsa_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Invalid ECDSA key format"); |
| } |
| // Check the key parameters. |
| if (EC_KEY_check_key(ecdsa_key) != kBsslOk) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Invalid ECDSA key format"); |
| } |
| return util::OkStatus(); |
| } |
| |
| // Converts the public portion of a given SubtleUtilBoringSSL::EcKey, |
| // `subtle_ec_key`, into an OpenSSL EC key, `openssl_ec_key`. |
| util::Status EcKeyToSslEcPublicKey( |
| const SubtleUtilBoringSSL::EcKey& subtle_ec_key, EC_KEY* openssl_ec_key) { |
| if (openssl_ec_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "`openssl_ec_key` arg cannot be NULL"); |
| } |
| |
| util::StatusOr<internal::SslUniquePtr<EC_GROUP>> group = |
| internal::EcGroupFromCurveType(subtle_ec_key.curve); |
| if (!group.ok()) { |
| return group.status(); |
| } |
| |
| util::StatusOr<internal::SslUniquePtr<EC_POINT>> ec_point = |
| internal::GetEcPoint(subtle_ec_key.curve, subtle_ec_key.pub_x, |
| subtle_ec_key.pub_y); |
| if (!ec_point.ok()) { |
| return ec_point.status(); |
| } |
| |
| // Set key's group and EC point. |
| if (!EC_KEY_set_group(openssl_ec_key, group->get())) { |
| return util::Status( |
| absl::StatusCode::kInternal, |
| absl::StrCat("Failed to set key group from EC group for curve ", |
| EnumToString(subtle_ec_key.curve))); |
| } |
| |
| if (!EC_KEY_set_public_key(openssl_ec_key, ec_point->get())) { |
| return util::Status(absl::StatusCode::kInternal, |
| "Failed to set public key"); |
| } |
| |
| return util::OkStatus(); |
| } |
| |
| // Converts a given SubtleUtilBoringSSL::EcKey, `subtle_ec_key`, into an OpenSSL |
| // EC key, `openssl_ec_key`. |
| util::Status EcKeyToSslEcPrivateKey( |
| const SubtleUtilBoringSSL::EcKey& subtle_ec_key, EC_KEY* openssl_ec_key) { |
| if (openssl_ec_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "`openssl_ec_key` arg cannot be NULL"); |
| } |
| util::Status status = EcKeyToSslEcPublicKey(subtle_ec_key, openssl_ec_key); |
| if (!status.ok()) { |
| return status; |
| } |
| |
| util::StatusOr<internal::SslUniquePtr<BIGNUM>> x = |
| internal::StringToBignum(absl::string_view( |
| reinterpret_cast<const char*>(subtle_ec_key.priv.data()), |
| subtle_ec_key.priv.size())); |
| if (!x.ok()) { |
| return x.status(); |
| } |
| |
| if (!EC_KEY_set_private_key(openssl_ec_key, x->get())) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Failed to set private key"); |
| } |
| return VerifyEcdsaKey(openssl_ec_key); |
| } |
| |
| // Converts an OpenSSL BIO (i.e., basic IO stream), `bio`, into a string. |
| util::StatusOr<std::string> ConvertBioToString(BIO* bio) { |
| BUF_MEM* mem = nullptr; |
| BIO_get_mem_ptr(bio, &mem); |
| std::string pem_material; |
| if (mem->data && mem->length) { |
| pem_material.assign(mem->data, mem->length); |
| } |
| if (pem_material.empty()) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Failed to retrieve key material from BIO"); |
| } |
| return pem_material; |
| } |
| |
| size_t ScalarSizeInBytes(const EC_GROUP* group) { |
| return BN_num_bytes(EC_GROUP_get0_order(group)); |
| } |
| |
| size_t FieldElementSizeInBytes(const EC_GROUP* group) { |
| unsigned degree_bits = EC_GROUP_get_degree(group); |
| return (degree_bits + 7) / 8; |
| } |
| |
| // We explicitly set a failing passphrase callback function to make sure no |
| // default callback routine is used. |
| static int FailingPassphraseCallback(char* buf, int buf_size, int rwflag, |
| void* u) { |
| return -1; |
| } |
| |
| } // namespace |
| |
| util::StatusOr<std::unique_ptr<internal::RsaPublicKey>> |
| PemParser::ParseRsaPublicKey(absl::string_view pem_serialized_key) { |
| // Read the RSA key into EVP_PKEY. |
| internal::SslUniquePtr<BIO> rsa_key_bio(BIO_new(BIO_s_mem())); |
| BIO_write(rsa_key_bio.get(), pem_serialized_key.data(), |
| pem_serialized_key.size()); |
| |
| internal::SslUniquePtr<EVP_PKEY> evp_rsa_key( |
| PEM_read_bio_PUBKEY(rsa_key_bio.get(), /*x=*/nullptr, |
| &FailingPassphraseCallback, /*u=*/nullptr)); |
| |
| if (evp_rsa_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "PEM Public Key parsing failed"); |
| } |
| // No need to free bssl_rsa_key after use. |
| const RSA* bssl_rsa_key = EVP_PKEY_get0_RSA(evp_rsa_key.get()); |
| util::Status verification_res = internal::RsaCheckPublicKey(bssl_rsa_key); |
| if (!verification_res.ok()) { |
| return verification_res; |
| } |
| |
| // Get the public key parameters. |
| const BIGNUM *n_bn, *e_bn, *d_bn; |
| RSA_get0_key(bssl_rsa_key, &n_bn, &e_bn, &d_bn); |
| |
| // Public key should not have d_bn set. |
| if (d_bn != nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Invalid RSA Public Key format"); |
| } |
| |
| // We are only interested in e and n. |
| auto n_str = internal::BignumToString(n_bn, BN_num_bytes(n_bn)); |
| auto e_str = internal::BignumToString(e_bn, BN_num_bytes(e_bn)); |
| if (!n_str.ok()) { |
| return n_str.status(); |
| } |
| if (!e_str.ok()) { |
| return e_str.status(); |
| } |
| auto rsa_public_key = absl::make_unique<internal::RsaPublicKey>(); |
| rsa_public_key->e = *std::move(e_str); |
| rsa_public_key->n = *std::move(n_str); |
| |
| return std::move(rsa_public_key); |
| } |
| |
| util::StatusOr<std::unique_ptr<internal::RsaPrivateKey>> |
| PemParser::ParseRsaPrivateKey(absl::string_view pem_serialized_key) { |
| // Read the private key into EVP_PKEY. |
| internal::SslUniquePtr<BIO> rsa_key_bio(BIO_new(BIO_s_mem())); |
| BIO_write(rsa_key_bio.get(), pem_serialized_key.data(), |
| pem_serialized_key.size()); |
| |
| // BoringSSL APIs to parse the PEM data. |
| internal::SslUniquePtr<EVP_PKEY> evp_rsa_key( |
| PEM_read_bio_PrivateKey(rsa_key_bio.get(), /*x=*/nullptr, |
| &FailingPassphraseCallback, /*u=*/nullptr)); |
| |
| if (evp_rsa_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "PEM Private Key parsing failed"); |
| } |
| |
| // No need to free bssl_rsa_key after use. |
| const RSA* bssl_rsa_key = EVP_PKEY_get0_RSA(evp_rsa_key.get()); |
| |
| auto is_valid_key = VerifyRsaKey(bssl_rsa_key); |
| if (!is_valid_key.ok()) { |
| return is_valid_key; |
| } |
| |
| const BIGNUM *n_bn, *e_bn, *d_bn; |
| RSA_get0_key(bssl_rsa_key, &n_bn, &e_bn, &d_bn); |
| |
| // Save exponents. |
| auto rsa_private_key = absl::make_unique<internal::RsaPrivateKey>(); |
| auto n_str = internal::BignumToString(n_bn, BN_num_bytes(n_bn)); |
| auto e_str = internal::BignumToString(e_bn, BN_num_bytes(e_bn)); |
| auto d_str = internal::BignumToSecretData(d_bn, BN_num_bytes(d_bn)); |
| if (!n_str.ok()) { |
| return n_str.status(); |
| } |
| if (!e_str.ok()) { |
| return e_str.status(); |
| } |
| if (!d_str.ok()) { |
| return d_str.status(); |
| } |
| rsa_private_key->n = *std::move(n_str); |
| rsa_private_key->e = *std::move(e_str); |
| rsa_private_key->d = *std::move(d_str); |
| |
| // Save factors. |
| const BIGNUM *p_bn, *q_bn; |
| RSA_get0_factors(bssl_rsa_key, &p_bn, &q_bn); |
| auto p_str = internal::BignumToSecretData(p_bn, BN_num_bytes(p_bn)); |
| auto q_str = internal::BignumToSecretData(q_bn, BN_num_bytes(q_bn)); |
| if (!p_str.ok()) { |
| return p_str.status(); |
| } |
| if (!q_str.ok()) { |
| return q_str.status(); |
| } |
| rsa_private_key->p = *std::move(p_str); |
| rsa_private_key->q = *std::move(q_str); |
| |
| // Save CRT parameters. |
| const BIGNUM *dp_bn, *dq_bn, *crt_bn; |
| RSA_get0_crt_params(bssl_rsa_key, &dp_bn, &dq_bn, &crt_bn); |
| auto dp_str = internal::BignumToSecretData(dp_bn, BN_num_bytes(dp_bn)); |
| auto dq_str = internal::BignumToSecretData(dq_bn, BN_num_bytes(dq_bn)); |
| auto crt_str = internal::BignumToSecretData(crt_bn, BN_num_bytes(crt_bn)); |
| if (!dp_str.ok()) { |
| return dp_str.status(); |
| } |
| if (!dq_str.ok()) { |
| return dq_str.status(); |
| } |
| if (!crt_str.ok()) { |
| return crt_str.status(); |
| } |
| rsa_private_key->dp = *std::move(dp_str); |
| rsa_private_key->dq = *std::move(dq_str); |
| rsa_private_key->crt = *std::move(crt_str); |
| |
| return std::move(rsa_private_key); |
| } |
| |
| util::StatusOr<std::string> PemParser::WriteRsaPublicKey( |
| const internal::RsaPublicKey& rsa_public_key) { |
| auto rsa_statusor = internal::RsaPublicKeyToRsa(rsa_public_key); |
| if (!rsa_statusor.ok()) { |
| return rsa_statusor.status(); |
| } |
| |
| internal::SslUniquePtr<RSA> rsa = *std::move(rsa_statusor); |
| internal::SslUniquePtr<BIO> bio(BIO_new(BIO_s_mem())); |
| if (!PEM_write_bio_RSA_PUBKEY(bio.get(), rsa.get())) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Failed to write openssl RSA key to write bio"); |
| } |
| return ConvertBioToString(bio.get()); |
| } |
| |
| util::StatusOr<std::string> PemParser::WriteRsaPrivateKey( |
| const internal::RsaPrivateKey& rsa_private_key) { |
| auto rsa_statusor = internal::RsaPrivateKeyToRsa(rsa_private_key); |
| if (!rsa_statusor.ok()) { |
| return rsa_statusor.status(); |
| } |
| |
| internal::SslUniquePtr<RSA> rsa = *std::move(rsa_statusor); |
| internal::SslUniquePtr<EVP_PKEY> evp(EVP_PKEY_new()); |
| EVP_PKEY_set1_RSA(evp.get(), rsa.get()); |
| |
| internal::SslUniquePtr<BIO> bio(BIO_new(BIO_s_mem())); |
| if (!PEM_write_bio_PrivateKey(bio.get(), evp.get(), |
| /*enc=*/nullptr, /*kstr=*/nullptr, |
| /*klen=*/0, |
| /*cb=*/nullptr, /*u=*/nullptr)) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Failed to write openssl RSA key to write bio"); |
| } |
| return ConvertBioToString(bio.get()); |
| } |
| |
| util::StatusOr<std::unique_ptr<SubtleUtilBoringSSL::EcKey>> |
| PemParser::ParseEcPublicKey(absl::string_view pem_serialized_key) { |
| // Read the ECDSA key into EVP_PKEY. |
| internal::SslUniquePtr<BIO> ecdsa_key_bio(BIO_new(BIO_s_mem())); |
| BIO_write(ecdsa_key_bio.get(), pem_serialized_key.data(), |
| pem_serialized_key.size()); |
| |
| internal::SslUniquePtr<EVP_PKEY> evp_ecdsa_key( |
| PEM_read_bio_PUBKEY(ecdsa_key_bio.get(), /*x=*/nullptr, |
| &FailingPassphraseCallback, /*u=*/nullptr)); |
| |
| if (evp_ecdsa_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "PEM Public Key parsing failed"); |
| } |
| // No need to free bssl_ecdsa_key after use. |
| EC_KEY* bssl_ecdsa_key = EVP_PKEY_get0_EC_KEY(evp_ecdsa_key.get()); |
| auto is_valid = VerifyEcdsaKey(bssl_ecdsa_key); |
| if (!is_valid.ok()) { |
| return is_valid; |
| } |
| |
| // Get the public key parameters. |
| const EC_POINT* public_point = EC_KEY_get0_public_key(bssl_ecdsa_key); |
| const EC_GROUP* ec_group = EC_KEY_get0_group(bssl_ecdsa_key); |
| internal::SslUniquePtr<BIGNUM> x_coordinate(BN_new()); |
| internal::SslUniquePtr<BIGNUM> y_coordinate(BN_new()); |
| EC_POINT_get_affine_coordinates(ec_group, public_point, x_coordinate.get(), |
| y_coordinate.get(), nullptr); |
| |
| // Convert public key parameters and construct Subtle ECKey |
| util::StatusOr<std::string> x_string = internal::BignumToString( |
| x_coordinate.get(), FieldElementSizeInBytes(ec_group)); |
| if (!x_string.ok()) { |
| return x_string.status(); |
| } |
| util::StatusOr<std::string> y_string = internal::BignumToString( |
| y_coordinate.get(), FieldElementSizeInBytes(ec_group)); |
| if (!y_string.ok()) { |
| return y_string.status(); |
| } |
| util::StatusOr<EllipticCurveType> curve = |
| internal::CurveTypeFromEcGroup(ec_group); |
| if (!curve.ok()) { |
| return curve.status(); |
| } |
| |
| auto ecdsa_public_key = absl::make_unique<SubtleUtilBoringSSL::EcKey>(); |
| ecdsa_public_key->pub_x = *std::move(x_string); |
| ecdsa_public_key->pub_y = *std::move(y_string); |
| ecdsa_public_key->curve = *std::move(curve); |
| |
| return std::move(ecdsa_public_key); |
| } |
| |
| util::StatusOr<std::unique_ptr<SubtleUtilBoringSSL::EcKey>> |
| PemParser::ParseEcPrivateKey(absl::string_view pem_serialized_key) { |
| internal::SslUniquePtr<BIO> ecdsa_key_bio(BIO_new(BIO_s_mem())); |
| BIO_write(ecdsa_key_bio.get(), pem_serialized_key.data(), |
| pem_serialized_key.size()); |
| |
| internal::SslUniquePtr<EVP_PKEY> evp_ecdsa_key( |
| PEM_read_bio_PrivateKey(ecdsa_key_bio.get(), /*x=*/nullptr, |
| &FailingPassphraseCallback, /*u=*/nullptr)); |
| |
| if (evp_ecdsa_key == nullptr) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "PEM Private Key parsing failed"); |
| } |
| |
| // No need to free bssl_ecdsa_key after use. |
| EC_KEY* bssl_ecdsa_key = EVP_PKEY_get0_EC_KEY(evp_ecdsa_key.get()); |
| util::Status verify_result = VerifyEcdsaKey(bssl_ecdsa_key); |
| if (!verify_result.ok()) { |
| return verify_result; |
| } |
| |
| internal::SslUniquePtr<BIGNUM> x_coordinate(BN_new()); |
| internal::SslUniquePtr<BIGNUM> y_coordinate(BN_new()); |
| const EC_POINT* public_point = EC_KEY_get0_public_key(bssl_ecdsa_key); |
| const EC_GROUP* ec_group = EC_KEY_get0_group(bssl_ecdsa_key); |
| EC_POINT_get_affine_coordinates(ec_group, public_point, x_coordinate.get(), |
| y_coordinate.get(), nullptr); |
| |
| const BIGNUM* priv_key = EC_KEY_get0_private_key(bssl_ecdsa_key); |
| util::StatusOr<util::SecretData> priv = |
| internal::BignumToSecretData(priv_key, ScalarSizeInBytes(ec_group)); |
| if (!priv.ok()) { |
| return priv.status(); |
| } |
| util::StatusOr<std::string> x_string = internal::BignumToString( |
| x_coordinate.get(), FieldElementSizeInBytes(ec_group)); |
| if (!x_string.ok()) { |
| return x_string.status(); |
| } |
| util::StatusOr<std::string> y_string = internal::BignumToString( |
| y_coordinate.get(), FieldElementSizeInBytes(ec_group)); |
| if (!y_string.ok()) { |
| return y_string.status(); |
| } |
| util::StatusOr<EllipticCurveType> curve = |
| internal::CurveTypeFromEcGroup(ec_group); |
| if (!curve.ok()) { |
| return curve.status(); |
| } |
| |
| auto ecdsa_private_key = absl::make_unique<SubtleUtilBoringSSL::EcKey>(); |
| ecdsa_private_key->pub_x = *std::move(x_string); |
| ecdsa_private_key->pub_y = *std::move(y_string); |
| ecdsa_private_key->priv = *std::move(priv); |
| ecdsa_private_key->curve = *std::move(curve); |
| |
| return std::move(ecdsa_private_key); |
| } |
| |
| util::StatusOr<std::string> PemParser::WriteEcPublicKey( |
| const SubtleUtilBoringSSL::EcKey& ec_key) { |
| internal::SslUniquePtr<EC_KEY> openssl_ec_key(EC_KEY_new()); |
| util::Status status = EcKeyToSslEcPublicKey(ec_key, openssl_ec_key.get()); |
| if (!status.ok()) { |
| return status; |
| } |
| internal::SslUniquePtr<BIO> bio(BIO_new(BIO_s_mem())); |
| if (!PEM_write_bio_EC_PUBKEY(bio.get(), openssl_ec_key.get())) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Failed to write openssl EC key to write bio"); |
| } |
| return ConvertBioToString(bio.get()); |
| } |
| |
| util::StatusOr<std::string> PemParser::WriteEcPrivateKey( |
| const SubtleUtilBoringSSL::EcKey& ec_key) { |
| internal::SslUniquePtr<EC_KEY> openssl_ec_key(EC_KEY_new()); |
| util::Status status = EcKeyToSslEcPrivateKey(ec_key, openssl_ec_key.get()); |
| if (!status.ok()) { |
| return status; |
| } |
| internal::SslUniquePtr<BIO> bio(BIO_new(BIO_s_mem())); |
| if (!PEM_write_bio_ECPrivateKey(bio.get(), openssl_ec_key.get(), |
| /*enc=*/nullptr, /*kstr=*/nullptr, /*klen=*/0, |
| /*cb=*/nullptr, /*u=*/nullptr)) { |
| return util::Status(absl::StatusCode::kInvalidArgument, |
| "Failed to write openssl EC key to write bio"); |
| } |
| return ConvertBioToString(bio.get()); |
| } |
| |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |