blob: 1dac7b0019465563623cd31ca5792ff41f7437e9 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// 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/internal/ec_util.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "openssl/bn.h"
#include "openssl/ec.h"
#include "openssl/ecdsa.h"
#include "openssl/evp.h"
#include "tink/internal/bn_util.h"
#include "tink/internal/err_util.h"
#include "tink/internal/fips_utils.h"
#include "tink/internal/ssl_unique_ptr.h"
#include "tink/subtle/common_enums.h"
#include "tink/subtle/random.h"
#include "tink/subtle/subtle_util.h"
#include "tink/util/secret_data.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
namespace crypto {
namespace tink {
namespace internal {
namespace {
using ::crypto::tink::subtle::EcPointFormat;
using ::crypto::tink::subtle::EllipticCurveType;
// Encodes the given `point` to string, according to a `conversion_form`.
util::StatusOr<std::string> SslEcPointEncode(
EC_GROUP *group, const EC_POINT *point,
point_conversion_form_t conversion_form) {
// Get the buffer size first passing a NULL buffer.
size_t buffer_size =
EC_POINT_point2oct(group, point, conversion_form,
/*buf=*/nullptr, /*len=*/0, /*ctx=*/nullptr);
if (buffer_size == 0) {
return util::Status(absl::StatusCode::kInternal,
"EC_POINT_point2oct failed");
}
std::string encoded_point;
subtle::ResizeStringUninitialized(&encoded_point, buffer_size);
size_t size =
EC_POINT_point2oct(group, point, conversion_form,
reinterpret_cast<uint8_t *>(&encoded_point[0]),
buffer_size, /*ctx=*/nullptr);
if (size == 0) {
return util::Status(absl::StatusCode::kInternal,
"EC_POINT_point2oct failed");
}
return encoded_point;
}
// Returns an EC_POINT from `group`, and encoded (bigendian string
// representation of BIGNUMs) point coordinates `pubx`, `puby`.
util::StatusOr<SslUniquePtr<EC_POINT>> SslGetEcPointFromCoordinates(
const EC_GROUP *group, absl::string_view pubx, absl::string_view puby) {
util::StatusOr<SslUniquePtr<BIGNUM>> bn_x = StringToBignum(pubx);
if (!bn_x.ok()) {
return bn_x.status();
}
util::StatusOr<SslUniquePtr<BIGNUM>> bn_y = StringToBignum(puby);
if (!bn_y.ok()) {
return bn_y.status();
}
SslUniquePtr<EC_POINT> pub_key(EC_POINT_new(group));
// In BoringSSL and OpenSSL > 1.1.0 EC_POINT_set_affine_coordinates_GFp
// already checkes if the point is on the curve.
if (EC_POINT_set_affine_coordinates_GFp(group, pub_key.get(), bn_x->get(),
bn_y->get(), nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal,
"EC_POINT_set_affine_coordinates_GFp failed");
}
return std::move(pub_key);
}
// Returns an EC_POINT from an `encoded` point with format `format` and curve
// type `curve`. `format` is either COMPRESSED or UNCOMPRESSED.
util::StatusOr<SslUniquePtr<EC_POINT>> SslGetEcPointFromEncoded(
EllipticCurveType curve, EcPointFormat format, absl::string_view encoded) {
if (format != EcPointFormat::UNCOMPRESSED &&
format != EcPointFormat::COMPRESSED) {
return util::Status(
absl::StatusCode::kInvalidArgument,
absl::StrCat("Invalid format ", subtle::EnumToString(format)));
}
util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
if (!group.ok()) {
return group.status();
}
util::StatusOr<int32_t> encoding_size =
EcPointEncodingSizeInBytes(curve, format);
if (!encoding_size.ok()) {
return encoding_size.status();
}
if (encoded.size() != *encoding_size) {
return util::Status(absl::StatusCode::kInternal,
absl::StrCat("Encoded point's size is ", encoded.size(),
" bytes; expected ", *encoding_size));
}
// Check starting byte.
if (format == EcPointFormat::UNCOMPRESSED &&
static_cast<int>(encoded[0]) != 0x04) {
return util::Status(
absl::StatusCode::kInternal,
"Uncompressed point should start with 0x04, but input doesn't");
} else if (format == EcPointFormat::COMPRESSED &&
static_cast<int>(encoded[0]) != 0x03 &&
static_cast<int>(encoded[0]) != 0x02) {
return util::Status(absl::StatusCode::kInternal,
"Compressed point should start with either 0x02 or "
"0x03, but input doesn't");
}
SslUniquePtr<EC_POINT> point(EC_POINT_new(group->get()));
if (EC_POINT_oct2point(group->get(), point.get(),
reinterpret_cast<const uint8_t *>(encoded.data()),
encoded.size(), nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal,
"EC_POINT_toc2point failed");
}
// Check that point is on curve.
if (EC_POINT_is_on_curve(group->get(), point.get(), nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal, "Point is not on curve");
}
return std::move(point);
}
// OpenSSL/BoringSSL's EC_POINT as a pair of BIGNUMs.
struct EcPointCoordinates {
SslUniquePtr<BIGNUM> x;
SslUniquePtr<BIGNUM> y;
};
// Returns a given `point` as a pair of BIGNUMs. Precondition: `group` and
// `point` are not null.
util::StatusOr<EcPointCoordinates> SslGetEcPointCoordinates(
const EC_GROUP *group, const EC_POINT *point) {
EcPointCoordinates coordinates = {
SslUniquePtr<BIGNUM>(BN_new()),
SslUniquePtr<BIGNUM>(BN_new()),
};
if (coordinates.x == nullptr || coordinates.y == nullptr) {
return util::Status(absl::StatusCode::kInternal,
"Unable to allocate memory for the point coordinates");
}
if (EC_POINT_get_affine_coordinates_GFp(group, point, coordinates.x.get(),
coordinates.y.get(), nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal,
"EC_POINT_get_affine_coordinates_GFp failed");
}
return std::move(coordinates);
}
size_t ScalarSizeInBytes(const EC_GROUP *group) {
return BN_num_bytes(EC_GROUP_get0_order(group));
}
size_t SslEcFieldSizeInBytes(const EC_GROUP *group) {
unsigned degree_bits = EC_GROUP_get_degree(group);
return (degree_bits + 7) / 8;
}
// Given an OpenSSL/BoringSSL key EC_KEY `key` and curve type `curve` return an
// EcKey.
util::StatusOr<EcKey> EcKeyFromSslEcKey(EllipticCurveType curve,
const EC_KEY &key) {
util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
if (!group.ok()) {
return group.status();
}
const BIGNUM *priv_key = EC_KEY_get0_private_key(&key);
const EC_POINT *pub_key = EC_KEY_get0_public_key(&key);
util::StatusOr<EcPointCoordinates> pub_key_bns =
SslGetEcPointCoordinates(group->get(), pub_key);
if (!pub_key_bns.ok()) {
return pub_key_bns.status();
}
const int kFieldElementSizeInBytes = SslEcFieldSizeInBytes(group->get());
util::StatusOr<std::string> pub_x_str =
BignumToString(pub_key_bns->x.get(), kFieldElementSizeInBytes);
if (!pub_x_str.ok()) {
return pub_x_str.status();
}
util::StatusOr<std::string> pub_y_str =
BignumToString(pub_key_bns->y.get(), kFieldElementSizeInBytes);
if (!pub_y_str.ok()) {
return pub_y_str.status();
}
util::StatusOr<util::SecretData> priv_key_data =
BignumToSecretData(priv_key, ScalarSizeInBytes(group->get()));
if (!priv_key_data.ok()) {
return priv_key_data.status();
}
EcKey ec_key = {
/*curve=*/curve,
/*pub_x=*/*std::move(pub_x_str),
/*pub_y=*/*std::move(pub_y_str),
/*priv=*/*std::move(priv_key_data),
};
return ec_key;
}
enum SslEvpPkeyType {
kX25519Key = EVP_PKEY_X25519,
kEd25519Key = EVP_PKEY_ED25519
};
// Returns a new EVP_PKEY key from the given `key_type`.
util::StatusOr<SslUniquePtr<EVP_PKEY>> SslNewEvpKey(SslEvpPkeyType key_type) {
EVP_PKEY *private_key = nullptr;
SslUniquePtr<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new_id(key_type, /*e=*/nullptr));
if (pctx == nullptr) {
return util::Status(
absl::StatusCode::kInternal,
absl::StrCat("EVP_PKEY_CTX_new_id failed for id ", key_type));
}
if (EVP_PKEY_keygen_init(pctx.get()) != 1) {
return util::Status(absl::StatusCode::kInternal,
"EVP_PKEY_keygen_init failed");
}
if (EVP_PKEY_keygen(pctx.get(), &private_key) != 1) {
return util::Status(absl::StatusCode::kInternal, "EVP_PKEY_keygen failed");
}
return {SslUniquePtr<EVP_PKEY>(private_key)};
}
// Given a private EVP_PKEY `evp_key` of key type `key_type` fills `priv_key`
// and `pub_key` with raw private and public keys, respectively.
util::Status SslNewKeyPairFromEcKey(SslEvpPkeyType key_type,
const EVP_PKEY &evp_key,
absl::Span<uint8_t> priv_key,
absl::Span<uint8_t> pub_key) {
size_t len = priv_key.size();
if (EVP_PKEY_get_raw_private_key(&evp_key, priv_key.data(), &len) != 1) {
return util::Status(absl::StatusCode::kInternal,
"EVP_PKEY_get_raw_private_key failed");
}
if (len != priv_key.size()) {
return util::Status(absl::StatusCode::kInternal,
absl::StrCat("Invalid private key size; expected ",
priv_key.size(), " got ", len));
}
len = pub_key.size();
if (EVP_PKEY_get_raw_public_key(&evp_key, pub_key.data(), &len) != 1) {
return util::Status(absl::StatusCode::kInternal,
"EVP_PKEY_get_raw_public_key failed");
}
if (len != pub_key.size()) {
return util::Status(absl::StatusCode::kInternal,
absl::StrCat("Invalid public key size; expected ",
pub_key.size(), " got ", len));
}
return util::OkStatus();
}
util::StatusOr<std::string> SslEcdsaSignatureToBytes(
const ECDSA_SIG *ecdsa_signature) {
if (ecdsa_signature == nullptr) {
return util::Status(absl::StatusCode::kInvalidArgument,
"ECDSA signature is null");
}
uint8_t *der = nullptr;
int der_len = i2d_ECDSA_SIG(ecdsa_signature, &der);
if (der_len <= 0) {
return util::Status(absl::StatusCode::kInternal, "i2d_ECDSA_SIG failed");
}
auto result = std::string(reinterpret_cast<char *>(der), der_len);
OPENSSL_free(der);
return result;
}
} // namespace
util::StatusOr<int32_t> EcFieldSizeInBytes(EllipticCurveType curve_type) {
if (curve_type == EllipticCurveType::CURVE25519) {
return 32;
}
util::StatusOr<SslUniquePtr<EC_GROUP>> ec_group =
EcGroupFromCurveType(curve_type);
if (!ec_group.ok()) {
return ec_group.status();
}
return SslEcFieldSizeInBytes(ec_group->get());
}
util::StatusOr<int32_t> EcPointEncodingSizeInBytes(EllipticCurveType curve_type,
EcPointFormat point_format) {
util::StatusOr<int32_t> coordinate_size = EcFieldSizeInBytes(curve_type);
if (!coordinate_size.ok()) {
return coordinate_size.status();
}
if (curve_type == EllipticCurveType::CURVE25519) {
return coordinate_size;
}
if (*coordinate_size == 0) {
return util::Status(absl::StatusCode::kInvalidArgument,
absl::StrCat("Unsupported elliptic curve type: ",
EnumToString(curve_type)));
}
switch (point_format) {
case EcPointFormat::UNCOMPRESSED:
return 2 * (*coordinate_size) + 1;
case EcPointFormat::COMPRESSED:
return (*coordinate_size) + 1;
case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
return 2 * (*coordinate_size);
default:
return util::Status(
absl::StatusCode::kInvalidArgument,
absl::StrCat("Unsupported elliptic curve point format: ",
EnumToString(point_format)));
}
}
util::StatusOr<EcKey> NewEcKey(EllipticCurveType curve_type) {
if (curve_type == EllipticCurveType::CURVE25519) {
util::StatusOr<std::unique_ptr<X25519Key>> key = NewX25519Key();
if (!key.ok()) {
return key.status();
}
return EcKeyFromX25519Key(key->get());
}
util::StatusOr<SslUniquePtr<EC_GROUP>> group =
EcGroupFromCurveType(curve_type);
if (!group.ok()) {
return group.status();
}
SslUniquePtr<EC_KEY> key(EC_KEY_new());
if (key.get() == nullptr) {
return util::Status(absl::StatusCode::kInternal, "EC_KEY_new failed");
}
EC_KEY_set_group(key.get(), group->get());
EC_KEY_generate_key(key.get());
return EcKeyFromSslEcKey(curve_type, *key);
}
util::StatusOr<EcKey> NewEcKey(EllipticCurveType curve_type,
const util::SecretData &secret_seed) {
// EC_KEY_derive_from_secret() is neither defined in the version of BoringSSL
// used when FIPS-only mode is enabled at compile time, nor currently
// implemented for OpenSSL.
#if defined(TINK_USE_ONLY_FIPS)
return util::Status(
absl::StatusCode::kUnimplemented,
"Deriving EC keys from a secret seed is not allowed in FIPS mode");
#elif !defined(OPENSSL_IS_BORINGSSL)
return util::Status(
absl::StatusCode::kUnimplemented,
"Deriving EC keys from a secret seed is not supported with OpenSSL");
#else
if (IsFipsModeEnabled()) {
return util::Status(
absl::StatusCode::kInternal,
"Deriving EC keys from a secret seed is not allowed in FIPS mode");
}
if (curve_type == EllipticCurveType::CURVE25519) {
return util::Status(
absl::StatusCode::kInternal,
"Creating a X25519 key from a secret seed is not supported");
}
util::StatusOr<SslUniquePtr<EC_GROUP>> group =
EcGroupFromCurveType(curve_type);
if (!group.ok()) {
return group.status();
}
SslUniquePtr<EC_KEY> key(EC_KEY_derive_from_secret(
group->get(), secret_seed.data(), secret_seed.size()));
if (key.get() == nullptr) {
return util::Status(absl::StatusCode::kInternal,
"EC_KEY_derive_from_secret failed");
}
return EcKeyFromSslEcKey(curve_type, *key);
#endif
}
util::StatusOr<std::unique_ptr<X25519Key>> NewX25519Key() {
util::StatusOr<SslUniquePtr<EVP_PKEY>> private_key =
SslNewEvpKey(SslEvpPkeyType::kX25519Key);
if (!private_key.ok()) {
return private_key.status();
}
auto key = absl::make_unique<X25519Key>();
util::Status res = SslNewKeyPairFromEcKey(
SslEvpPkeyType::kX25519Key, **private_key,
absl::MakeSpan(key->private_key, X25519KeyPrivKeySize()),
absl::MakeSpan(key->public_value, X25519KeyPubKeySize()));
if (!res.ok()) {
return res;
}
return std::move(key);
}
EcKey EcKeyFromX25519Key(const X25519Key *x25519_key) {
EcKey ec_key;
ec_key.curve = subtle::EllipticCurveType::CURVE25519;
// Curve25519 public key is x, not (x,y).
ec_key.pub_x =
std::string(reinterpret_cast<const char *>(x25519_key->public_value),
X25519KeyPubKeySize());
ec_key.priv = util::SecretData(std::begin(x25519_key->private_key),
std::end(x25519_key->private_key));
return ec_key;
}
util::StatusOr<std::unique_ptr<Ed25519Key>> NewEd25519Key() {
util::SecretData seed =
subtle::Random::GetRandomKeyBytes(Ed25519KeyPrivKeySize());
return NewEd25519Key(seed);
}
util::StatusOr<std::unique_ptr<Ed25519Key>> NewEd25519Key(
const util::SecretData &secret_seed) {
if (secret_seed.size() != Ed25519KeyPrivKeySize()) {
return util::Status(
absl::StatusCode::kInvalidArgument,
absl::StrCat("Invalid seed of length ", secret_seed.size(),
"; expected ", Ed25519KeyPrivKeySize()));
}
// In BoringSSL this calls ED25519_keypair_from_seed. Accessing the public key
// with EVP_PKEY_get_raw_public_key returns the last 32 bytes of the private
// key stored by BoringSSL.
SslUniquePtr<EVP_PKEY> priv_key(EVP_PKEY_new_raw_private_key(
SslEvpPkeyType::kEd25519Key, nullptr, secret_seed.data(),
Ed25519KeyPrivKeySize()));
if (priv_key == nullptr) {
return util::Status(absl::StatusCode::kInternal,
"EVP_PKEY_new_raw_private_key failed");
}
auto key = absl::make_unique<Ed25519Key>();
subtle::ResizeStringUninitialized(&key->private_key, Ed25519KeyPrivKeySize());
subtle::ResizeStringUninitialized(&key->public_key, Ed25519KeyPubKeySize());
uint8_t *priv_key_ptr = reinterpret_cast<uint8_t *>(&key->private_key[0]);
uint8_t *pub_key_ptr = reinterpret_cast<uint8_t *>(&key->public_key[0]);
// The EVP_PKEY interface returns only the first 32 bytes of the private key.
util::Status res = SslNewKeyPairFromEcKey(
SslEvpPkeyType::kEd25519Key, *priv_key,
absl::MakeSpan(priv_key_ptr, Ed25519KeyPrivKeySize()),
absl::MakeSpan(pub_key_ptr, Ed25519KeyPubKeySize()));
if (!res.ok()) {
return res;
}
return std::move(key);
}
util::StatusOr<std::unique_ptr<X25519Key>> X25519KeyFromEcKey(
const EcKey &ec_key) {
auto x25519_key = absl::make_unique<X25519Key>();
if (ec_key.curve != subtle::EllipticCurveType::CURVE25519) {
return util::Status(absl::StatusCode::kInvalidArgument,
"This key is not on curve 25519");
}
if (!ec_key.pub_y.empty()) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Invalid X25519 key. pub_y is unexpectedly set.");
}
// Curve25519 public key is x, not (x,y).
std::copy_n(ec_key.pub_x.begin(), X25519KeyPubKeySize(),
std::begin(x25519_key->public_value));
std::copy_n(ec_key.priv.begin(), X25519KeyPrivKeySize(),
std::begin(x25519_key->private_key));
return std::move(x25519_key);
}
util::StatusOr<util::SecretData> ComputeX25519SharedSecret(
EVP_PKEY *private_key, EVP_PKEY *peer_public_key) {
// Make sure the keys are actually X25519 keys.
if (EVP_PKEY_id(private_key) != SslEvpPkeyType::kX25519Key) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Invalid type for private key");
}
if (EVP_PKEY_id(peer_public_key) != SslEvpPkeyType::kX25519Key) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Invalid type for peer's public key");
}
internal::SslUniquePtr<EVP_PKEY_CTX> pctx(
EVP_PKEY_CTX_new(private_key, nullptr));
util::SecretData shared_secret(internal::X25519KeySharedKeySize());
size_t out_key_length = shared_secret.size();
if (EVP_PKEY_derive_init(pctx.get()) <= 0 ||
EVP_PKEY_derive_set_peer(pctx.get(), peer_public_key) <= 0 ||
EVP_PKEY_derive(pctx.get(), shared_secret.data(), &out_key_length) <= 0) {
return util::Status(absl::StatusCode::kInternal,
"Secret generation failed");
}
return shared_secret;
}
util::StatusOr<std::unique_ptr<X25519Key>> X25519KeyFromPrivateKey(
const util::SecretData &private_key) {
if (private_key.size() != X25519KeyPrivKeySize()) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Invalid length for private key");
}
internal::SslUniquePtr<EVP_PKEY> pkey(
EVP_PKEY_new_raw_private_key(SslEvpPkeyType::kX25519Key, nullptr,
private_key.data(), private_key.size()));
auto key = absl::make_unique<X25519Key>();
util::Status res = SslNewKeyPairFromEcKey(
SslEvpPkeyType::kX25519Key, *pkey,
absl::MakeSpan(key->private_key, X25519KeyPrivKeySize()),
absl::MakeSpan(key->public_value, X25519KeyPubKeySize()));
if (!res.ok()) {
return res;
}
return std::move(key);
}
util::StatusOr<std::string> EcPointEncode(EllipticCurveType curve,
EcPointFormat format,
const EC_POINT *point) {
util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
if (!group.ok()) {
return group.status();
}
if (EC_POINT_is_on_curve(group->get(), point, nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal, "Point is not on curve");
}
switch (format) {
case EcPointFormat::UNCOMPRESSED: {
return SslEcPointEncode(group->get(), point,
POINT_CONVERSION_UNCOMPRESSED);
}
case EcPointFormat::COMPRESSED: {
return SslEcPointEncode(group->get(), point, POINT_CONVERSION_COMPRESSED);
}
case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED: {
util::StatusOr<EcPointCoordinates> ec_point_xy =
SslGetEcPointCoordinates(group->get(), point);
if (!ec_point_xy.ok()) {
return ec_point_xy.status();
}
const int kCurveSizeInBytes = SslEcFieldSizeInBytes(group->get());
std::string encoded_point;
subtle::ResizeStringUninitialized(&encoded_point, 2 * kCurveSizeInBytes);
util::Status res = BignumToBinaryPadded(
absl::MakeSpan(&encoded_point[0], kCurveSizeInBytes),
ec_point_xy->x.get());
if (!res.ok()) {
return util::Status(
absl::StatusCode::kInternal,
absl::StrCat(res.message(), " serializing the x coordinate"));
}
res = BignumToBinaryPadded(
absl::MakeSpan(&encoded_point[kCurveSizeInBytes], kCurveSizeInBytes),
ec_point_xy->y.get());
if (!res.ok()) {
return util::Status(
absl::StatusCode::kInternal,
absl::StrCat(res.message(), " serializing the y coordinate"));
}
return encoded_point;
}
default:
return util::Status(absl::StatusCode::kInternal,
"Unsupported point format");
}
}
util::StatusOr<SslUniquePtr<EC_POINT>> EcPointDecode(
EllipticCurveType curve, EcPointFormat format, absl::string_view encoded) {
switch (format) {
case EcPointFormat::UNCOMPRESSED:
case EcPointFormat::COMPRESSED:
return SslGetEcPointFromEncoded(curve, format, encoded);
case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED: {
util::StatusOr<SslUniquePtr<EC_GROUP>> group =
EcGroupFromCurveType(curve);
if (!group.ok()) {
return group.status();
}
const int kCurveSizeInBytes = SslEcFieldSizeInBytes(group->get());
if (encoded.size() != 2 * kCurveSizeInBytes) {
return util::Status(
absl::StatusCode::kInternal,
absl::StrCat("Encoded point's size is ", encoded.size(),
" bytes; expected ", 2 * kCurveSizeInBytes));
}
// SslGetEcPoint already checks if the point is on curve so we can return
// directly.
return SslGetEcPointFromCoordinates(group->get(),
encoded.substr(0, kCurveSizeInBytes),
encoded.substr(kCurveSizeInBytes));
}
default:
return util::Status(absl::StatusCode::kInternal, "Unsupported format");
}
}
util::StatusOr<SslUniquePtr<EC_GROUP>> EcGroupFromCurveType(
EllipticCurveType curve_type) {
EC_GROUP *ec_group = nullptr;
switch (curve_type) {
case EllipticCurveType::NIST_P256: {
ec_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
break;
}
case EllipticCurveType::NIST_P384: {
ec_group = EC_GROUP_new_by_curve_name(NID_secp384r1);
break;
}
case EllipticCurveType::NIST_P521: {
ec_group = EC_GROUP_new_by_curve_name(NID_secp521r1);
break;
}
default:
return util::Status(absl::StatusCode::kUnimplemented,
"Unsupported elliptic curve");
}
if (ec_group == nullptr) {
return util::Status(absl::StatusCode::kInternal,
"EC_GROUP_new_by_curve_name failed");
}
return {SslUniquePtr<EC_GROUP>(ec_group)};
}
util::StatusOr<EllipticCurveType> CurveTypeFromEcGroup(const EC_GROUP *group) {
if (group == nullptr) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Null group provided");
}
switch (EC_GROUP_get_curve_name(group)) {
case NID_X9_62_prime256v1:
return EllipticCurveType::NIST_P256;
case NID_secp384r1:
return EllipticCurveType::NIST_P384;
case NID_secp521r1:
return EllipticCurveType::NIST_P521;
default:
return util::Status(absl::StatusCode::kUnimplemented,
"Unsupported elliptic curve");
}
}
util::StatusOr<SslUniquePtr<EC_POINT>> GetEcPoint(EllipticCurveType curve,
absl::string_view pubx,
absl::string_view puby) {
util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
if (!group.ok()) {
return group.status();
}
return SslGetEcPointFromCoordinates(group->get(), pubx, puby);
}
util::StatusOr<util::SecretData> ComputeEcdhSharedSecret(
EllipticCurveType curve, const BIGNUM *priv_key, const EC_POINT *pub_key) {
util::StatusOr<internal::SslUniquePtr<EC_GROUP>> priv_group =
internal::EcGroupFromCurveType(curve);
if (!priv_group.ok()) {
return priv_group.status();
}
if (EC_POINT_is_on_curve(priv_group->get(), pub_key, /*ctx=*/nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal,
absl::StrCat("Public key is not on curve ",
subtle::EnumToString(curve)));
}
// Compute the shared point and make sure it is on `curve`.
internal::SslUniquePtr<EC_POINT> shared_point(
EC_POINT_new(priv_group->get()));
if (EC_POINT_mul(priv_group->get(), shared_point.get(), /*n=*/nullptr,
pub_key, priv_key, /*ctx=*/nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal,
"Point multiplication failed");
}
if (EC_POINT_is_on_curve(priv_group->get(), shared_point.get(),
/*ctx=*/nullptr) != 1) {
return util::Status(absl::StatusCode::kInternal,
absl::StrCat("Shared point is not on curve ",
subtle::EnumToString(curve)));
}
util::StatusOr<EcPointCoordinates> shared_point_coordinates =
SslGetEcPointCoordinates(priv_group->get(), shared_point.get());
if (!shared_point_coordinates.ok()) {
return shared_point_coordinates.status();
}
// We need only the x coordinate.
return internal::BignumToSecretData(shared_point_coordinates->x.get(),
SslEcFieldSizeInBytes(priv_group->get()));
}
util::StatusOr<std::string> EcSignatureIeeeToDer(const EC_GROUP *group,
absl::string_view ieee_sig) {
const size_t kFieldSizeInBytes = SslEcFieldSizeInBytes(group);
if (ieee_sig.size() != kFieldSizeInBytes * 2) {
return util::Status(absl::StatusCode::kInvalidArgument,
"Signature is not valid.");
}
util::StatusOr<SslUniquePtr<BIGNUM>> r =
internal::StringToBignum(ieee_sig.substr(0, kFieldSizeInBytes));
if (!r.ok()) {
return r.status();
}
util::StatusOr<SslUniquePtr<BIGNUM>> s =
internal::StringToBignum(ieee_sig.substr(kFieldSizeInBytes));
if (!s.ok()) {
return s.status();
}
internal::SslUniquePtr<ECDSA_SIG> ecdsa(ECDSA_SIG_new());
if (ECDSA_SIG_set0(ecdsa.get(), r->get(), s->get()) != 1) {
return util::Status(absl::StatusCode::kInternal, "ECDSA_SIG_set0 failed");
}
// ECDSA_SIG_set0 takes ownership of s and r's pointers.
r->release();
s->release();
return SslEcdsaSignatureToBytes(ecdsa.get());
}
} // namespace internal
} // namespace tink
} // namespace crypto