blob: bebc04e01e1eb6b74936947a889dd995fb0eb65d [file] [log] [blame]
// 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