blob: 6e2d3b61ab2dc238cd60f773c441fad0d4ff190d [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 "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "openssl/base.h"
#include "openssl/bio.h"
#include "openssl/bn.h"
#include "openssl/evp.h"
#include "openssl/pem.h"
#include "openssl/rsa.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(util::error::INVALID_ARGUMENT,
"Invalid RSA key format");
}
// Check the key parameters.
if (RSA_check_key(rsa_key) != kBsslOk) {
return util::Status(util::error::INVALID_ARGUMENT,
"Invalid RSA 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 ConvertSubtleEcKeyToOpenSslEcPublicKey(
const SubtleUtilBoringSSL::EcKey& subtle_ec_key, EC_KEY* openssl_ec_key) {
if (openssl_ec_key == nullptr) {
return util::Status(util::error::INVALID_ARGUMENT,
"`openssl_ec_key` arg cannot be NULL");
}
// Set the key's group (EC curve).
auto group_statusor = SubtleUtilBoringSSL::GetEcGroup(subtle_ec_key.curve);
if (!group_statusor.ok()) {
return group_statusor.status();
}
bssl::UniquePtr<EC_GROUP> group(group_statusor.ValueOrDie());
if (group.get() == nullptr) {
return util::Status(
util::error::INTERNAL,
absl::StrCat("failed to set EC group to curve ", subtle_ec_key.curve));
}
if (!EC_KEY_set_group(openssl_ec_key, group.get())) {
return util::Status(
util::error::INTERNAL,
absl::StrCat("failed to set key group from EC group for curve ",
subtle_ec_key.curve));
}
// Create an EC point and initialize it from the key proto.
bssl::UniquePtr<EC_POINT> point(EC_POINT_new(group.get()));
if (!point.get()) {
return util::Status(util::error::INTERNAL, "failed to allocate EC_POINT");
}
bssl::UniquePtr<BIGNUM> x(BN_bin2bn(
reinterpret_cast<const unsigned char*>(subtle_ec_key.pub_x.data()),
subtle_ec_key.pub_x.length(), nullptr));
bssl::UniquePtr<BIGNUM> y(BN_bin2bn(
reinterpret_cast<const unsigned char*>(subtle_ec_key.pub_y.data()),
subtle_ec_key.pub_y.length(), nullptr));
if (!EC_POINT_set_affine_coordinates_GFp(group.get(), point.get(), x.get(),
y.get(), nullptr)) {
return util::Status(util::error::INVALID_ARGUMENT,
"failed to set affine coordinates");
}
if (!EC_POINT_is_on_curve(group.get(), point.get(), nullptr)) {
return util::Status(util::error::INVALID_ARGUMENT,
"failed to confirm EC point is on curve");
}
// Set the key's point from the EC point, created above.
if (!EC_KEY_set_public_key(openssl_ec_key, point.get())) {
return util::Status(util::error::INTERNAL, "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 ConvertSubtleEcKeyToOpenSslEcPrivateKey(
const SubtleUtilBoringSSL::EcKey& subtle_ec_key, EC_KEY* openssl_ec_key) {
if (openssl_ec_key == nullptr) {
return util::Status(util::error::INVALID_ARGUMENT,
"`openssl_ec_key` arg cannot be NULL");
}
util::Status status =
ConvertSubtleEcKeyToOpenSslEcPublicKey(subtle_ec_key, openssl_ec_key);
if (!status.ok()) {
return status;
}
bssl::UniquePtr<BIGNUM> x(BN_bin2bn(
reinterpret_cast<const unsigned char*>(subtle_ec_key.priv.data()),
subtle_ec_key.priv.size(), nullptr));
if (!EC_KEY_set_private_key(openssl_ec_key, x.get())) {
return util::Status(util::error::INVALID_ARGUMENT,
"failed to set private key");
}
if (!EC_KEY_check_key(openssl_ec_key)) {
return util::Status(util::error::INVALID_ARGUMENT,
"failed private key check");
}
return util::OkStatus();
}
// 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(util::error::INVALID_ARGUMENT,
"failed to retrieve key material from BIO");
}
return pem_material;
}
} // namespace
// static.
util::StatusOr<std::unique_ptr<SubtleUtilBoringSSL::RsaPublicKey>>
PemParser::ParseRsaPublicKey(absl::string_view pem_serialized_key) {
// Read the RSA key into EVP_PKEY.
bssl::UniquePtr<BIO> rsa_key_bio(BIO_new(BIO_s_mem()));
BIO_write(rsa_key_bio.get(), pem_serialized_key.data(),
pem_serialized_key.size());
bssl::UniquePtr<EVP_PKEY> evp_rsa_key(PEM_read_bio_PUBKEY(
rsa_key_bio.get(), /*x=*/nullptr, /*cb=*/nullptr, /*u=*/nullptr));
if (evp_rsa_key == nullptr) {
return util::Status(util::error::INVALID_ARGUMENT,
"PEM Public Key parsing failed");
}
// No need to free bssl_rsa_key after use.
RSA *bssl_rsa_key = EVP_PKEY_get0_RSA(evp_rsa_key.get());
auto is_valid = VerifyRsaKey(bssl_rsa_key);
if (!is_valid.ok()) {
return is_valid;
}
// Get the public key paramters.
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(util::error::INVALID_ARGUMENT,
"Invalid RSA Public Key format");
}
// We are only interested in e and n.
auto n_str_statusor = SubtleUtilBoringSSL::bn2str(n_bn, BN_num_bytes(n_bn));
auto e_str_statusor = SubtleUtilBoringSSL::bn2str(e_bn, BN_num_bytes(e_bn));
if (!n_str_statusor.ok()) return n_str_statusor.status();
if (!e_str_statusor.ok()) return e_str_statusor.status();
auto rsa_public_key = absl::make_unique<SubtleUtilBoringSSL::RsaPublicKey>();
rsa_public_key->e = std::move(e_str_statusor.ValueOrDie());
rsa_public_key->n = std::move(n_str_statusor.ValueOrDie());
return rsa_public_key;
}
// static.
util::StatusOr<std::unique_ptr<SubtleUtilBoringSSL::RsaPrivateKey>>
PemParser::ParseRsaPrivateKey(absl::string_view pem_serialized_key) {
// Read the private key into EVP_PKEY.
bssl::UniquePtr<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.
bssl::UniquePtr<EVP_PKEY> evp_rsa_key(PEM_read_bio_PrivateKey(
rsa_key_bio.get(), /*x=*/nullptr, /*cb=*/nullptr, /*u=*/nullptr));
if (evp_rsa_key == nullptr) {
return util::Status(util::error::INVALID_ARGUMENT,
"PEM Private Key parsing failed");
}
// No need to free bssl_rsa_key after use.
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<SubtleUtilBoringSSL::RsaPrivateKey>();
auto n_str = SubtleUtilBoringSSL::bn2str(n_bn, BN_num_bytes(n_bn));
auto e_str = SubtleUtilBoringSSL::bn2str(e_bn, BN_num_bytes(e_bn));
auto d_str =
SubtleUtilBoringSSL::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.ValueOrDie());
rsa_private_key->e = std::move(e_str.ValueOrDie());
rsa_private_key->d = std::move(d_str.ValueOrDie());
// Save factors.
const BIGNUM *p_bn, *q_bn;
RSA_get0_factors(bssl_rsa_key, &p_bn, &q_bn);
auto p_str =
SubtleUtilBoringSSL::BignumToSecretData(p_bn, BN_num_bytes(p_bn));
auto q_str =
SubtleUtilBoringSSL::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.ValueOrDie());
rsa_private_key->q = std::move(q_str.ValueOrDie());
// 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 =
SubtleUtilBoringSSL::BignumToSecretData(dp_bn, BN_num_bytes(dp_bn));
auto dq_str =
SubtleUtilBoringSSL::BignumToSecretData(dq_bn, BN_num_bytes(dq_bn));
auto crt_str =
SubtleUtilBoringSSL::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.ValueOrDie());
rsa_private_key->dq = std::move(dq_str.ValueOrDie());
rsa_private_key->crt = std::move(crt_str.ValueOrDie());
return rsa_private_key;
}
util::StatusOr<std::unique_ptr<SubtleUtilBoringSSL::EcKey>>
PemParser::ParseEcPublicKey(absl::string_view pem_serialized_key) {
return util::Status(util::error::UNIMPLEMENTED,
"PEM EC Public Key parsing is unimplemented");
}
util::StatusOr<std::unique_ptr<SubtleUtilBoringSSL::EcKey>>
PemParser::ParseEcPrivateKey(absl::string_view pem_serialized_key) {
return util::Status(util::error::UNIMPLEMENTED,
"PEM EC Private Key parsing is unimplemented");
}
util::StatusOr<std::string> PemParser::WriteEcPublicKey(
const SubtleUtilBoringSSL::EcKey& ec_key) {
bssl::UniquePtr<EC_KEY> openssl_ec_key(EC_KEY_new());
util::Status status =
ConvertSubtleEcKeyToOpenSslEcPublicKey(ec_key, openssl_ec_key.get());
if (!status.ok()) {
return status;
}
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
if (!PEM_write_bio_EC_PUBKEY(bio.get(), openssl_ec_key.get())) {
return util::Status(util::error::INVALID_ARGUMENT,
"failed to write openssl EC key to write bio");
}
return ConvertBioToString(bio.get());
}
util::StatusOr<std::string> PemParser::WriteEcPrivateKey(
const SubtleUtilBoringSSL::EcKey& ec_key) {
bssl::UniquePtr<EC_KEY> openssl_ec_key(EC_KEY_new());
util::Status status =
ConvertSubtleEcKeyToOpenSslEcPrivateKey(ec_key, openssl_ec_key.get());
if (!status.ok()) {
return status;
}
bssl::UniquePtr<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(util::error::INVALID_ARGUMENT,
"failed to write openssl EC key to write bio");
}
return ConvertBioToString(bio.get());
}
} // namespace subtle
} // namespace tink
} // namespace crypto