chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,695 @@
// Written by Nils Larsch for the OpenSSL project.
// Copyright (c) 2000-2003 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/ec.h>
#include <limits.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include "../bytestring/internal.h"
#include "../fipsmodule/ec/internal.h"
#include "../internal.h"
#include "internal.h"
static const CBS_ASN1_TAG kParametersTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0;
static const CBS_ASN1_TAG kPublicKeyTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
// TODO(https://crbug.com/boringssl/497): Allow parsers to specify a list of
// acceptable groups, so parsers don't have to pull in all four.
typedef const EC_GROUP *(*ec_group_func)(void);
static const ec_group_func kAllGroups[] = {
&EC_group_p224, &EC_group_p256, &EC_group_p384,
&EC_group_p521, &EC_group_secp256k1,
};
EC_KEY *EC_KEY_parse_private_key(CBS *cbs, const EC_GROUP *group) {
CBS ec_private_key, private_key;
uint64_t version;
if (!CBS_get_asn1(cbs, &ec_private_key, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&ec_private_key, &version) || version != 1 ||
!CBS_get_asn1(&ec_private_key, &private_key, CBS_ASN1_OCTETSTRING)) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
return NULL;
}
// Parse the optional parameters field.
EC_KEY *ret = NULL;
BIGNUM *priv_key = NULL;
enum ECParametersType paramType = UNKNOWN_EC_PARAMETERS;
if (CBS_peek_asn1_tag(&ec_private_key, kParametersTag)) {
// Per SEC 1, as an alternative to omitting it, one is allowed to specify
// this field and put in a NULL to mean inheriting this value. This was
// omitted in a previous version of this logic without problems, so leave it
// unimplemented.
CBS child;
if (!CBS_get_asn1(&ec_private_key, &child, kParametersTag)) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
goto err;
}
const EC_GROUP *inner_group = EC_KEY_parse_parameters_and_type(&child, &paramType);
if (inner_group == NULL || paramType == UNKNOWN_EC_PARAMETERS) {
goto err;
}
if (group == NULL) {
group = inner_group;
} else if (EC_GROUP_cmp(group, inner_group, NULL) != 0) {
// If a group was supplied externally, it must match.
OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
goto err;
}
if (CBS_len(&child) != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
goto err;
}
}
if (group == NULL) {
OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
goto err;
}
ret = EC_KEY_new();
if (ret == NULL || !EC_KEY_set_group(ret, group)) {
goto err;
}
if(paramType == SPECIFIED_CURVE_EC_PARAMETERS) {
ret->group_decoded_from_explicit_params = 1;
} else {
ret->group_decoded_from_explicit_params = 0;
}
// Although RFC 5915 specifies the length of the key, OpenSSL historically
// got this wrong, so accept any length. See upstream's
// 30cd4ff294252c4b6a4b69cbef6a5b4117705d22.
priv_key = BN_bin2bn(CBS_data(&private_key), CBS_len(&private_key), NULL);
ret->pub_key = EC_POINT_new(group);
if (priv_key == NULL || ret->pub_key == NULL ||
!EC_KEY_set_private_key(ret, priv_key)) {
goto err;
}
if (CBS_peek_asn1_tag(&ec_private_key, kPublicKeyTag)) {
CBS child, public_key;
uint8_t padding;
if (!CBS_get_asn1(&ec_private_key, &child, kPublicKeyTag) ||
!CBS_get_asn1(&child, &public_key, CBS_ASN1_BITSTRING) ||
// As in a SubjectPublicKeyInfo, the byte-encoded public key is then
// encoded as a BIT STRING with bits ordered as in the DER encoding.
!CBS_get_u8(&public_key, &padding) || padding != 0 ||
// Explicitly check |public_key| is non-empty to save the conversion
// form later.
CBS_len(&public_key) == 0 ||
!EC_POINT_oct2point(group, ret->pub_key, CBS_data(&public_key),
CBS_len(&public_key), NULL) ||
CBS_len(&child) != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
goto err;
}
// Save the point conversion form.
// TODO(davidben): Consider removing this.
ret->conv_form =
(point_conversion_form_t)(CBS_data(&public_key)[0] & ~0x01);
} else {
// Compute the public key instead.
if (!ec_point_mul_scalar_base(group, &ret->pub_key->raw,
&ret->priv_key->scalar)) {
goto err;
}
// Remember the original private-key-only encoding.
// TODO(davidben): Consider removing this.
ret->enc_flag |= EC_PKEY_NO_PUBKEY;
}
if (CBS_len(&ec_private_key) != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
goto err;
}
// Ensure the resulting key is valid.
if (!EC_KEY_check_key(ret)) {
goto err;
}
BN_free(priv_key);
return ret;
err:
EC_KEY_free(ret);
BN_free(priv_key);
return NULL;
}
int EC_KEY_marshal_private_key(CBB *cbb, const EC_KEY *key,
unsigned enc_flags) {
if (key == NULL || key->group == NULL || key->priv_key == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
CBB ec_private_key, private_key;
if (!CBB_add_asn1(cbb, &ec_private_key, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1_uint64(&ec_private_key, 1 /* version */) ||
!CBB_add_asn1(&ec_private_key, &private_key, CBS_ASN1_OCTETSTRING) ||
!BN_bn2cbb_padded(&private_key,
BN_num_bytes(EC_GROUP_get0_order(key->group)),
EC_KEY_get0_private_key(key))) {
OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
return 0;
}
if (!(enc_flags & EC_PKEY_NO_PARAMETERS)) {
CBB child;
if (!CBB_add_asn1(&ec_private_key, &child, kParametersTag) ||
!EC_KEY_marshal_curve_name(&child, key->group) ||
!CBB_flush(&ec_private_key)) {
OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
return 0;
}
}
// TODO(fork): replace this flexibility with sensible default?
if (!(enc_flags & EC_PKEY_NO_PUBKEY) && key->pub_key != NULL) {
CBB child, public_key;
if (!CBB_add_asn1(&ec_private_key, &child, kPublicKeyTag) ||
!CBB_add_asn1(&child, &public_key, CBS_ASN1_BITSTRING) ||
// As in a SubjectPublicKeyInfo, the byte-encoded public key is then
// encoded as a BIT STRING with bits ordered as in the DER encoding.
!CBB_add_u8(&public_key, 0 /* padding */) ||
!EC_POINT_point2cbb(&public_key, key->group, key->pub_key,
key->conv_form, NULL) ||
!CBB_flush(&ec_private_key)) {
OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
return 0;
}
}
if (!CBB_flush(cbb)) {
OPENSSL_PUT_ERROR(EC, EC_R_ENCODE_ERROR);
return 0;
}
return 1;
}
// kPrimeFieldOID is the encoding of 1.2.840.10045.1.1.
static const uint8_t kPrimeField[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01, 0x01};
struct explicit_prime_curve {
CBS prime, a, b, base_x, base_y, order;
};
static int parse_explicit_prime_curve(CBS *in,
struct explicit_prime_curve *out) {
// See RFC 3279, section 2.3.5. Note that RFC 3279 calls this structure an
// ECParameters while RFC 5480 calls it a SpecifiedECDomain.
CBS params, field_id, field_type, curve, base, cofactor;
int has_cofactor;
uint64_t version;
if (!CBS_get_asn1(in, &params, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&params, &version) || version != 1 ||
!CBS_get_asn1(&params, &field_id, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&field_id, &field_type, CBS_ASN1_OBJECT) ||
CBS_len(&field_type) != sizeof(kPrimeField) ||
OPENSSL_memcmp(CBS_data(&field_type), kPrimeField, sizeof(kPrimeField)) !=
0 ||
!CBS_get_asn1(&field_id, &out->prime, CBS_ASN1_INTEGER) ||
!CBS_is_unsigned_asn1_integer(&out->prime) || CBS_len(&field_id) != 0 ||
!CBS_get_asn1(&params, &curve, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&curve, &out->a, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&curve, &out->b, CBS_ASN1_OCTETSTRING) ||
// |curve| has an optional BIT STRING seed which we ignore.
!CBS_get_optional_asn1(&curve, NULL, NULL, CBS_ASN1_BITSTRING) ||
CBS_len(&curve) != 0 ||
!CBS_get_asn1(&params, &base, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&params, &out->order, CBS_ASN1_INTEGER) ||
!CBS_is_unsigned_asn1_integer(&out->order) ||
!CBS_get_optional_asn1(&params, &cofactor, &has_cofactor,
CBS_ASN1_INTEGER) ||
CBS_len(&params) != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
return 0;
}
if (has_cofactor) {
// We only support prime-order curves so the cofactor must be one.
if (CBS_len(&cofactor) != 1 || CBS_data(&cofactor)[0] != 1) {
OPENSSL_PUT_ERROR(EC, EC_R_UNKNOWN_GROUP);
return 0;
}
}
// Require that the base point use uncompressed form.
uint8_t form;
if (!CBS_get_u8(&base, &form) || form != POINT_CONVERSION_UNCOMPRESSED) {
OPENSSL_PUT_ERROR(EC, EC_R_INVALID_FORM);
return 0;
}
if (CBS_len(&base) % 2 != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
return 0;
}
size_t field_len = CBS_len(&base) / 2;
CBS_init(&out->base_x, CBS_data(&base), field_len);
CBS_init(&out->base_y, CBS_data(&base) + field_len, field_len);
return 1;
}
// integers_equal returns one if |bytes| is a big-endian encoding of |bn|, and
// zero otherwise.
static int integers_equal(const CBS *bytes, const BIGNUM *bn) {
// Although, in SEC 1, Field-Element-to-Octet-String has a fixed width,
// OpenSSL mis-encodes the |a| and |b|, so we tolerate any number of leading
// zeros. (This matters for P-521 whose |b| has a leading 0.)
CBS copy = *bytes;
while (CBS_len(&copy) > 0 && CBS_data(&copy)[0] == 0) {
CBS_skip(&copy, 1);
}
if (CBS_len(&copy) > EC_MAX_BYTES) {
return 0;
}
uint8_t buf[EC_MAX_BYTES];
if (!BN_bn2bin_padded(buf, CBS_len(&copy), bn)) {
ERR_clear_error();
return 0;
}
return CBS_mem_equal(&copy, buf, CBS_len(&copy));
}
EC_GROUP *EC_KEY_parse_curve_name(CBS *cbs) {
CBS named_curve;
if (!CBS_get_asn1(cbs, &named_curve, CBS_ASN1_OBJECT)) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
return NULL;
}
// Look for a matching curve.
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kAllGroups); i++) {
const EC_GROUP *group = kAllGroups[i]();
if (CBS_mem_equal(&named_curve, group->oid, group->oid_len)) {
return (EC_GROUP *)group;
}
}
OPENSSL_PUT_ERROR(EC, EC_R_UNKNOWN_GROUP);
return NULL;
}
int EC_KEY_marshal_curve_name(CBB *cbb, const EC_GROUP *group) {
if (group->oid_len == 0) {
OPENSSL_PUT_ERROR(EC, EC_R_UNKNOWN_GROUP);
return 0;
}
CBB child;
return CBB_add_asn1(cbb, &child, CBS_ASN1_OBJECT) &&
CBB_add_bytes(&child, group->oid, group->oid_len) && //
CBB_flush(cbb);
}
EC_GROUP *EC_KEY_parse_parameters(CBS *cbs) {
enum ECParametersType paramType = UNKNOWN_EC_PARAMETERS;
return EC_KEY_parse_parameters_and_type(cbs, &paramType);
}
EC_GROUP *EC_KEY_parse_parameters_and_type(CBS *cbs, enum ECParametersType *paramType) {
GUARD_PTR(paramType);
const EC_GROUP *ret = NULL;
*paramType = UNKNOWN_EC_PARAMETERS;
if (!CBS_peek_asn1_tag(cbs, CBS_ASN1_SEQUENCE)) {
ret = EC_KEY_parse_curve_name(cbs);
if(ret) {
*paramType = NAMED_CURVE_EC_PARAMETERS;
}
return (EC_GROUP *)ret;
}
// OpenSSL sometimes produces ECPrivateKeys or ECPublicKey with explicitly-encoded versions
// of named curves.
struct explicit_prime_curve curve;
if (!parse_explicit_prime_curve(cbs, &curve)) {
return NULL;
}
BIGNUM *p = BN_new(), *a = BN_new(), *b = BN_new(), *x = BN_new(),
*y = BN_new();
if (p == NULL || a == NULL || b == NULL || x == NULL || y == NULL) {
goto err;
}
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kAllGroups); i++) {
const EC_GROUP *group = kAllGroups[i]();
if (!integers_equal(&curve.order, EC_GROUP_get0_order(group))) {
continue;
}
// The order alone uniquely identifies the group, but we check the other
// parameters to avoid misinterpreting the group.
if (!EC_GROUP_get_curve_GFp(group, p, a, b, NULL)) {
goto err;
}
if (!integers_equal(&curve.prime, p) || !integers_equal(&curve.a, a) ||
!integers_equal(&curve.b, b)) {
break;
}
if (!EC_POINT_get_affine_coordinates_GFp(
group, EC_GROUP_get0_generator(group), x, y, NULL)) {
goto err;
}
if (!integers_equal(&curve.base_x, x) ||
!integers_equal(&curve.base_y, y)) {
break;
}
ret = group;
*paramType = SPECIFIED_CURVE_EC_PARAMETERS;
break;
}
if (ret == NULL) {
OPENSSL_PUT_ERROR(EC, EC_R_UNKNOWN_GROUP);
}
err:
BN_free(p);
BN_free(a);
BN_free(b);
BN_free(x);
BN_free(y);
return (EC_GROUP *)ret;
}
int EC_POINT_point2cbb(CBB *out, const EC_GROUP *group, const EC_POINT *point,
point_conversion_form_t form, BN_CTX *ctx) {
size_t len = EC_POINT_point2oct(group, point, form, NULL, 0, ctx);
if (len == 0) {
return 0;
}
uint8_t *p;
return CBB_add_space(out, &p, len) &&
EC_POINT_point2oct(group, point, form, p, len, ctx) == len;
}
EC_KEY *d2i_ECPrivateKey(EC_KEY **out, const uint8_t **inp, long len) {
// This function treats its |out| parameter differently from other |d2i|
// functions. If supplied, take the group from |*out|.
const EC_GROUP *group = NULL;
if (out != NULL && *out != NULL) {
group = EC_KEY_get0_group(*out);
}
if (len < 0) {
OPENSSL_PUT_ERROR(EC, EC_R_DECODE_ERROR);
return NULL;
}
CBS cbs;
CBS_init(&cbs, *inp, (size_t)len);
EC_KEY *ret = EC_KEY_parse_private_key(&cbs, group);
if (ret == NULL) {
return NULL;
}
if (out != NULL) {
EC_KEY_free(*out);
*out = ret;
}
*inp = CBS_data(&cbs);
return ret;
}
int i2d_ECPrivateKey(const EC_KEY *key, uint8_t **outp) {
CBB cbb;
if (!CBB_init(&cbb, 0) ||
!EC_KEY_marshal_private_key(&cbb, key, EC_KEY_get_enc_flags(key))) {
CBB_cleanup(&cbb);
return -1;
}
return CBB_finish_i2d(&cbb, outp);
}
EC_KEY *d2i_ECParameters(EC_KEY **out_key, const uint8_t **inp, long len) {
if (len < 0) {
return NULL;
}
EC_GROUP *group = d2i_ECPKParameters(NULL, inp, len);
if (group == NULL) {
return NULL;
}
EC_KEY *ret = EC_KEY_new();
if (ret == NULL || !EC_KEY_set_group(ret, group)) {
EC_KEY_free(ret);
return NULL;
}
if (out_key != NULL) {
EC_KEY_free(*out_key);
*out_key = ret;
}
return ret;
}
EC_GROUP *d2i_ECPKParameters(EC_GROUP **out_group, const uint8_t **inp,
long len) {
if (inp == NULL || len < 0) {
return NULL;
}
CBS cbs;
CBS_init(&cbs, *inp, (size_t)len);
EC_GROUP *group = EC_KEY_parse_parameters(&cbs);
if (group == NULL) {
return NULL;
}
if (out_group != NULL) {
EC_GROUP_free(*out_group);
*out_group = group;
}
*inp = CBS_data(&cbs);
return group;
}
int i2d_ECParameters(const EC_KEY *key, uint8_t **outp) {
if (key == NULL || key->group == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
return i2d_ECPKParameters(key->group, outp);
}
int i2d_ECPKParameters(const EC_GROUP *group, uint8_t **outp) {
if (group == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
CBB cbb;
if (!CBB_init(&cbb, 0) || !EC_KEY_marshal_curve_name(&cbb, group)) {
CBB_cleanup(&cbb);
return -1;
}
return CBB_finish_i2d(&cbb, outp);
}
EC_GROUP *d2i_ECPKParameters_bio(BIO *bio, EC_GROUP **out_group) {
if (bio == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
uint8_t *data;
size_t len;
if (!BIO_read_asn1(bio, &data, &len, INT_MAX)) {
return NULL;
}
const uint8_t *ptr = data;
EC_GROUP *ret = d2i_ECPKParameters(out_group, &ptr, len);
OPENSSL_free(data);
return ret;
}
int i2d_ECPKParameters_bio(BIO *bio, const EC_GROUP *group) {
if (bio == NULL || group == NULL) {
OPENSSL_PUT_ERROR(PKCS7, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
uint8_t *out = NULL;
int len = i2d_ECPKParameters(group, &out);
if (out == NULL) {
return 0;
}
int ret = BIO_write_all(bio, out, len);
OPENSSL_free(out);
return ret;
}
EC_KEY *o2i_ECPublicKey(EC_KEY **keyp, const uint8_t **inp, long len) {
EC_KEY *ret = NULL;
if (keyp == NULL || *keyp == NULL || (*keyp)->group == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
ret = *keyp;
if (ret->pub_key == NULL &&
(ret->pub_key = EC_POINT_new(ret->group)) == NULL) {
return NULL;
}
if (!EC_POINT_oct2point(ret->group, ret->pub_key, *inp, len, NULL)) {
OPENSSL_PUT_ERROR(EC, ERR_R_EC_LIB);
return NULL;
}
// save the point conversion form
ret->conv_form = (point_conversion_form_t)(*inp[0] & ~0x01);
*inp += len;
return ret;
}
int i2o_ECPublicKey(const EC_KEY *key, uint8_t **outp) {
if (key == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
CBB cbb;
if (!CBB_init(&cbb, 0) || //
!EC_POINT_point2cbb(&cbb, key->group, key->pub_key, key->conv_form,
NULL)) {
CBB_cleanup(&cbb);
return -1;
}
int ret = CBB_finish_i2d(&cbb, outp);
// Historically, this function used the wrong return value on error.
return ret > 0 ? ret : 0;
}
size_t EC_get_builtin_curves(EC_builtin_curve *out_curves,
size_t max_num_curves) {
if (max_num_curves > OPENSSL_ARRAY_SIZE(kAllGroups)) {
max_num_curves = OPENSSL_ARRAY_SIZE(kAllGroups);
}
for (size_t i = 0; i < max_num_curves; i++) {
const EC_GROUP *group = kAllGroups[i]();
out_curves[i].nid = group->curve_name;
out_curves[i].comment = group->comment;
}
return OPENSSL_ARRAY_SIZE(kAllGroups);
}
static size_t EC_POINT_point2buf(const EC_GROUP *group, const EC_POINT *point,
point_conversion_form_t form, uint8_t **pbuf,
BN_CTX *ctx) {
size_t len;
uint8_t *buf;
len = EC_POINT_point2oct(group, point, form, NULL, 0, NULL);
if (len == 0) {
return 0;
}
buf = OPENSSL_malloc(len);
if (buf == NULL) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
return 0;
}
len = EC_POINT_point2oct(group, point, form, buf, len, ctx);
if (len == 0) {
OPENSSL_free(buf);
return 0;
}
*pbuf = buf;
return len;
}
BIGNUM *EC_POINT_point2bn(const EC_GROUP *group, const EC_POINT *point,
point_conversion_form_t form, BIGNUM *ret,
BN_CTX *ctx) {
size_t buf_len = 0;
uint8_t *buf;
buf_len = EC_POINT_point2buf(group, point, form, &buf, ctx);
if (buf_len == 0) {
return NULL;
}
ret = BN_bin2bn(buf, buf_len, ret);
OPENSSL_free(buf);
return ret;
}
EC_POINT *EC_POINT_bn2point(const EC_GROUP *group, const BIGNUM *bn,
EC_POINT *point, BN_CTX *ctx) {
if (group == NULL || bn == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
// Allocate buffer and length.
size_t buf_len = BN_num_bytes(bn);
if (buf_len == 0) {
// See https://github.com/openssl/openssl/issues/10258.
buf_len = 1;
}
uint8_t *buf = OPENSSL_malloc(buf_len);
if (buf == NULL) {
return NULL;
}
if (BN_bn2bin_padded(buf, buf_len, bn) < 0) {
OPENSSL_free(buf);
return NULL;
}
// Use the user-provided |point| if there is one. Otherwise, we allocate a new
// |EC_POINT| if |point| is NULL.
EC_POINT *ret;
if (point != NULL) {
ret = point;
} else {
ret = EC_POINT_new(group);
if (ret == NULL) {
OPENSSL_free(buf);
return NULL;
}
}
if (!EC_POINT_oct2point(group, ret, buf, buf_len, ctx)) {
if (ret != point) {
// If the user did not provide a |point|, we free the |EC_POINT| we
// allocated.
EC_POINT_free(ret);
ret = NULL;
}
}
OPENSSL_free(buf);
return ret;
}
int ECPKParameters_print(BIO *bio, const EC_GROUP *group, int offset) {
return 1;
}

View File

@@ -0,0 +1,85 @@
// Copyright (c) 2019, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/ec_key.h>
#include <string.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/digest.h>
#include <openssl/hkdf.h>
#include <openssl/mem.h>
#include "../fipsmodule/ec/internal.h"
EC_KEY *EC_KEY_derive_from_secret(const EC_GROUP *group, const uint8_t *secret,
size_t secret_len) {
#define EC_KEY_DERIVE_MAX_NAME_LEN 16
const char *name = EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
if (name == NULL || strlen(name) > EC_KEY_DERIVE_MAX_NAME_LEN) {
OPENSSL_PUT_ERROR(EC, EC_R_UNKNOWN_GROUP);
return NULL;
}
// Assemble a label string to provide some key separation in case |secret| is
// misused, but ultimately it's on the caller to ensure |secret| is suitably
// separated.
static const char kLabel[] = "derive EC key ";
char info[sizeof(kLabel) + EC_KEY_DERIVE_MAX_NAME_LEN];
OPENSSL_strlcpy(info, kLabel, sizeof(info));
OPENSSL_strlcat(info, name, sizeof(info));
// Generate 128 bits beyond the group order so the bias is at most 2^-128.
#define EC_KEY_DERIVE_EXTRA_BITS 128
#define EC_KEY_DERIVE_EXTRA_BYTES (EC_KEY_DERIVE_EXTRA_BITS / 8)
if (EC_GROUP_order_bits(group) <= EC_KEY_DERIVE_EXTRA_BITS + 8) {
// The reduction strategy below requires the group order be large enough.
// (The actual bound is a bit tighter, but our curves are much larger than
// 128-bit.)
OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
return NULL;
}
uint8_t derived[EC_KEY_DERIVE_EXTRA_BYTES + EC_MAX_BYTES];
size_t derived_len =
BN_num_bytes(EC_GROUP_get0_order(group)) + EC_KEY_DERIVE_EXTRA_BYTES;
assert(derived_len <= sizeof(derived));
if (!HKDF(derived, derived_len, EVP_sha256(), secret, secret_len,
/*salt=*/NULL, /*salt_len=*/0, (const uint8_t *)info,
strlen(info))) {
return NULL;
}
EC_KEY *key = EC_KEY_new();
BN_CTX *ctx = BN_CTX_new();
BIGNUM *priv = BN_bin2bn(derived, derived_len, NULL);
EC_POINT *pub = EC_POINT_new(group);
if (key == NULL || ctx == NULL || priv == NULL || pub == NULL ||
// Reduce |priv| with Montgomery reduction. First, convert "from"
// Montgomery form to compute |priv| * R^-1 mod |order|. This requires
// |priv| be under order * R, which is true if the group order is large
// enough. 2^(num_bytes(order)) < 2^8 * order, so:
//
// priv < 2^8 * order * 2^128 < order * order < order * R
!BN_from_montgomery(priv, priv, &group->order, ctx) ||
// Multiply by R^2 and do another Montgomery reduction to compute
// priv * R^-1 * R^2 * R^-1 = priv mod order.
!BN_to_montgomery(priv, priv, &group->order, ctx) ||
!EC_POINT_mul(group, pub, priv, NULL, NULL, ctx) ||
!EC_KEY_set_group(key, group) || !EC_KEY_set_public_key(key, pub) ||
!EC_KEY_set_private_key(key, priv)) {
EC_KEY_free(key);
key = NULL;
goto err;
}
err:
OPENSSL_cleanse(derived, sizeof(derived));
BN_CTX_free(ctx);
BN_free(priv);
EC_POINT_free(pub);
return key;
}

View File

@@ -0,0 +1,485 @@
// Copyright (c) 2020, Google Inc.
// SPDX-License-Identifier: ISC
#include <openssl/ec.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/nid.h>
#include <openssl/type_check.h>
#include <assert.h>
#include "internal.h"
#include "../fipsmodule/bn/internal.h"
#include "../fipsmodule/ec/internal.h"
#include "../internal.h"
// This file implements hash-to-curve, as described in RFC 9380.
//
// This hash-to-curve implementation is written generically with the
// expectation that we will eventually wish to support other curves. If it
// becomes a performance bottleneck, some possible optimizations by
// specializing it to the curve:
//
// - Rather than using a generic |felem_exp|, specialize the exponentation to
// c2 with a faster addition chain.
//
// - |felem_mul| and |felem_sqr| are indirect calls to generic Montgomery
// code. Given the few curves, we could specialize
// |map_to_curve_simple_swu|. But doing this reasonably without duplicating
// code in C is difficult. (C++ templates would be useful here.)
//
// - P-521's Z and c2 have small power-of-two absolute values. We could save
// two multiplications in SSWU. (Other curves have reasonable values of Z
// and inconvenient c2.) This is unlikely to be worthwhile without C++
// templates to make specializing more convenient.
// expand_message_xmd implements the operation described in section 5.3.1 of
// RFC 9380. It returns one on success and zero on error.
static int expand_message_xmd(const EVP_MD *md, uint8_t *out, size_t out_len,
const uint8_t *msg, size_t msg_len,
const uint8_t *dst, size_t dst_len) {
// See https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/issues/352
if (dst_len == 0) {
OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
int ret = 0;
const size_t block_size = EVP_MD_block_size(md);
const size_t md_size = EVP_MD_size(md);
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
// Long DSTs are hashed down to size. See section 5.3.3.
OPENSSL_STATIC_ASSERT(EVP_MAX_MD_SIZE < 256, hashed_DST_still_too_large)
uint8_t dst_buf[EVP_MAX_MD_SIZE];
if (dst_len >= 256) {
static const char kPrefix[] = "H2C-OVERSIZE-DST-";
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
!EVP_DigestUpdate(&ctx, kPrefix, sizeof(kPrefix) - 1) ||
!EVP_DigestUpdate(&ctx, dst, dst_len) ||
!EVP_DigestFinal_ex(&ctx, dst_buf, NULL)) {
goto err;
}
dst = dst_buf;
dst_len = md_size;
}
uint8_t dst_len_u8 = (uint8_t)dst_len;
// Compute b_0.
static const uint8_t kZeros[EVP_MAX_MD_BLOCK_SIZE] = {0};
// If |out_len| exceeds 16 bits then |i| will wrap below causing an error to
// be returned. This depends on the static assert above.
uint8_t l_i_b_str_zero[3] = {out_len >> 8, out_len, 0};
uint8_t b_0[EVP_MAX_MD_SIZE];
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
!EVP_DigestUpdate(&ctx, kZeros, block_size) ||
!EVP_DigestUpdate(&ctx, msg, msg_len) ||
!EVP_DigestUpdate(&ctx, l_i_b_str_zero, sizeof(l_i_b_str_zero)) ||
!EVP_DigestUpdate(&ctx, dst, dst_len) ||
!EVP_DigestUpdate(&ctx, &dst_len_u8, 1) ||
!EVP_DigestFinal_ex(&ctx, b_0, NULL)) {
goto err;
}
uint8_t b_i[EVP_MAX_MD_SIZE];
uint8_t i = 1;
while (out_len > 0) {
if (i == 0) {
// Input was too large.
OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
goto err;
}
if (i > 1) {
for (size_t j = 0; j < md_size; j++) {
b_i[j] ^= b_0[j];
}
} else {
OPENSSL_memcpy(b_i, b_0, md_size);
}
if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
!EVP_DigestUpdate(&ctx, b_i, md_size) ||
!EVP_DigestUpdate(&ctx, &i, 1) ||
!EVP_DigestUpdate(&ctx, dst, dst_len) ||
!EVP_DigestUpdate(&ctx, &dst_len_u8, 1) ||
!EVP_DigestFinal_ex(&ctx, b_i, NULL)) {
goto err;
}
size_t todo = out_len >= md_size ? md_size : out_len;
OPENSSL_memcpy(out, b_i, todo);
out += todo;
out_len -= todo;
i++;
}
ret = 1;
err:
EVP_MD_CTX_cleanup(&ctx);
return ret;
}
// num_bytes_to_derive determines the number of bytes to derive when hashing to
// a number modulo |modulus|. See the hash_to_field operation defined in
// section 5.2 of RFC 9380.
static int num_bytes_to_derive(size_t *out, const BIGNUM *modulus, unsigned k) {
size_t bits = BN_num_bits(modulus);
size_t L = (bits + k + 7) / 8;
// We require 2^(8*L) < 2^(2*bits - 2) <= n^2 so to fit in bounds for
// |felem_reduce| and |ec_scalar_reduce|. All defined hash-to-curve suites
// define |k| to be well under this bound. (|k| is usually around half of
// |p_bits|.)
if (L * 8 >= 2 * bits - 2 ||
L > 2 * EC_MAX_BYTES) {
assert(0);
OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
return 0;
}
*out = L;
return 1;
}
// hash_to_field implements the operation described in section 5.2
// of RFC 9380, with count = 2. |k| is the security factor.
static int hash_to_field2(const EC_GROUP *group, const EVP_MD *md,
EC_FELEM *out1, EC_FELEM *out2, const uint8_t *dst,
size_t dst_len, unsigned k, const uint8_t *msg,
size_t msg_len) {
size_t L;
uint8_t buf[4 * EC_MAX_BYTES];
if (!num_bytes_to_derive(&L, &group->field.N, k) ||
!expand_message_xmd(md, buf, 2 * L, msg, msg_len, dst, dst_len)) {
return 0;
}
BN_ULONG words[2 * EC_MAX_WORDS];
size_t num_words = 2 * group->field.N.width;
bn_big_endian_to_words(words, num_words, buf, L);
group->meth->felem_reduce(group, out1, words, num_words);
bn_big_endian_to_words(words, num_words, buf + L, L);
group->meth->felem_reduce(group, out2, words, num_words);
return 1;
}
// hash_to_scalar behaves like |hash_to_field2| but returns a value modulo the
// group order rather than a field element. |k| is the security factor.
static int hash_to_scalar(const EC_GROUP *group, const EVP_MD *md,
EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
unsigned k, const uint8_t *msg, size_t msg_len) {
const BIGNUM *order = EC_GROUP_get0_order(group);
size_t L;
uint8_t buf[EC_MAX_BYTES * 2];
if (!num_bytes_to_derive(&L, order, k) ||
!expand_message_xmd(md, buf, L, msg, msg_len, dst, dst_len)) {
return 0;
}
BN_ULONG words[2 * EC_MAX_WORDS];
size_t num_words = 2 * order->width;
bn_big_endian_to_words(words, num_words, buf, L);
ec_scalar_reduce(group, out, words, num_words);
return 1;
}
static inline void mul_A(const EC_GROUP *group, EC_FELEM *out,
const EC_FELEM *in) {
assert(group->a_is_minus3);
EC_FELEM tmp;
ec_felem_add(group, &tmp, in, in); // tmp = 2*in
ec_felem_add(group, &tmp, &tmp, &tmp); // tmp = 4*in
ec_felem_sub(group, out, in, &tmp); // out = -3*in
}
// sgn0 implements the operation described in section 4.1.2 of RFC 9380.
static BN_ULONG sgn0(const EC_GROUP *group, const EC_FELEM *a) {
uint8_t buf[EC_MAX_BYTES];
size_t len;
ec_felem_to_bytes(group, buf, &len, a);
return buf[len - 1] & 1;
}
OPENSSL_UNUSED static int is_3mod4(const EC_GROUP *group) {
return group->field.N.width > 0 && (group->field.N.d[0] & 3) == 3;
}
// sqrt_ratio_3mod4 implements the operation described in appendix F.2.1.2
// of RFC 9380.
static BN_ULONG sqrt_ratio_3mod4(const EC_GROUP *group, const EC_FELEM *Z,
const BN_ULONG *c1, size_t num_c1,
const EC_FELEM *c2, EC_FELEM *out_y,
const EC_FELEM *u, const EC_FELEM *v) {
assert(is_3mod4(group));
void (*const felem_mul)(const EC_GROUP *, EC_FELEM *r, const EC_FELEM *a,
const EC_FELEM *b) = group->meth->felem_mul;
void (*const felem_sqr)(const EC_GROUP *, EC_FELEM *r, const EC_FELEM *a) =
group->meth->felem_sqr;
EC_FELEM tv1, tv2, tv3, y1, y2;
felem_sqr(group, &tv1, v); // 1. tv1 = v^2
felem_mul(group, &tv2, u, v); // 2. tv2 = u * v
felem_mul(group, &tv1, &tv1, &tv2); // 3. tv1 = tv1 * tv2
group->meth->felem_exp(group, &y1, &tv1, c1, num_c1); // 4. y1 = tv1^c1
felem_mul(group, &y1, &y1, &tv2); // 5. y1 = y1 * tv2
felem_mul(group, &y2, &y1, c2); // 6. y2 = y1 * c2
felem_sqr(group, &tv3, &y1); // 7. tv3 = y1^2
felem_mul(group, &tv3, &tv3, v); // 8. tv3 = tv3 * v
// 9. isQR = tv3 == u
// 10. y = CMOV(y2, y1, isQR)
// 11. return (isQR, y)
//
// Note the specification's CMOV function and our |ec_felem_select| have the
// opposite argument order.
ec_felem_sub(group, &tv1, &tv3, u);
const BN_ULONG isQR = ~ec_felem_non_zero_mask(group, &tv1);
ec_felem_select(group, out_y, isQR, &y1, &y2);
return isQR;
}
// map_to_curve_simple_swu implements the operation described in section 6.6.2
// of RFC 9380, using the straight-line implementation in appendix F.2.
static void map_to_curve_simple_swu(const EC_GROUP *group, const EC_FELEM *Z,
const BN_ULONG *c1, size_t num_c1,
const EC_FELEM *c2, EC_JACOBIAN *out,
const EC_FELEM *u) {
// This function requires the prime be 3 mod 4, and that A = -3.
assert(is_3mod4(group));
assert(group->a_is_minus3);
void (*const felem_mul)(const EC_GROUP *, EC_FELEM *r, const EC_FELEM *a,
const EC_FELEM *b) = group->meth->felem_mul;
void (*const felem_sqr)(const EC_GROUP *, EC_FELEM *r, const EC_FELEM *a) =
group->meth->felem_sqr;
EC_FELEM tv1, tv2, tv3, tv4, tv5, tv6, x, y, y1;
felem_sqr(group, &tv1, u); // 1. tv1 = u^2
felem_mul(group, &tv1, Z, &tv1); // 2. tv1 = Z * tv1
felem_sqr(group, &tv2, &tv1); // 3. tv2 = tv1^2
ec_felem_add(group, &tv2, &tv2, &tv1); // 4. tv2 = tv2 + tv1
ec_felem_add(group, &tv3, &tv2, ec_felem_one(group)); // 5. tv3 = tv2 + 1
felem_mul(group, &tv3, &group->b, &tv3); // 6. tv3 = B * tv3
// 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
const BN_ULONG tv2_non_zero = ec_felem_non_zero_mask(group, &tv2);
ec_felem_neg(group, &tv4, &tv2);
ec_felem_select(group, &tv4, tv2_non_zero, &tv4, Z);
mul_A(group, &tv4, &tv4); // 8. tv4 = A * tv4
felem_sqr(group, &tv2, &tv3); // 9. tv2 = tv3^2
felem_sqr(group, &tv6, &tv4); // 10. tv6 = tv4^2
mul_A(group, &tv5, &tv6); // 11. tv5 = A * tv6
ec_felem_add(group, &tv2, &tv2, &tv5); // 12. tv2 = tv2 + tv5
felem_mul(group, &tv2, &tv2, &tv3); // 13. tv2 = tv2 * tv3
felem_mul(group, &tv6, &tv6, &tv4); // 14. tv6 = tv6 * tv4
felem_mul(group, &tv5, &group->b, &tv6); // 15. tv5 = B * tv6
ec_felem_add(group, &tv2, &tv2, &tv5); // 16. tv2 = tv2 + tv5
felem_mul(group, &x, &tv1, &tv3); // 17. x = tv1 * tv3
// 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
const BN_ULONG is_gx1_square =
sqrt_ratio_3mod4(group, Z, c1, num_c1, c2, &y1, &tv2, &tv6);
felem_mul(group, &y, &tv1, u); // 19. y = tv1 * u
felem_mul(group, &y, &y, &y1); // 20. y = y * y1
// 21. x = CMOV(x, tv3, is_gx1_square)
ec_felem_select(group, &x, is_gx1_square, &tv3, &x);
// 22. y = CMOV(y, y1, is_gx1_square)
ec_felem_select(group, &y, is_gx1_square, &y1, &y);
// 23. e1 = sgn0(u) == sgn0(y)
BN_ULONG sgn0_u = sgn0(group, u);
BN_ULONG sgn0_y = sgn0(group, &y);
BN_ULONG not_e1 = sgn0_u ^ sgn0_y;
not_e1 = ((BN_ULONG)0) - not_e1;
// 24. y = CMOV(-y, y, e1)
ec_felem_neg(group, &tv1, &y);
ec_felem_select(group, &y, not_e1, &tv1, &y);
// 25. x = x / tv4
//
// Our output is in projective coordinates, so rather than inverting |tv4|
// now, represent (x / tv4, y) as (x * tv4, y * tv4^3, tv4). This is much more
// efficient if the caller will do further computation on the output. (If the
// caller will immediately convert to affine coordinates, it is slightly less
// efficient, but only by a few field multiplications.)
felem_mul(group, &out->X, &x, &tv4);
felem_mul(group, &out->Y, &y, &tv6);
out->Z = tv4;
}
static int hash_to_curve(const EC_GROUP *group, const EVP_MD *md,
const EC_FELEM *Z, const EC_FELEM *c2, unsigned k,
EC_JACOBIAN *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len) {
EC_FELEM u0, u1;
if (!hash_to_field2(group, md, &u0, &u1, dst, dst_len, k, msg, msg_len)) {
return 0;
}
// Compute |c1| = (p - 3) / 4.
BN_ULONG c1[EC_MAX_WORDS];
size_t num_c1 = group->field.N.width;
if (!bn_copy_words(c1, num_c1, &group->field.N)) {
return 0;
}
bn_rshift_words(c1, c1, /*shift=*/2, /*num=*/num_c1);
EC_JACOBIAN Q0, Q1;
map_to_curve_simple_swu(group, Z, c1, num_c1, c2, &Q0, &u0);
map_to_curve_simple_swu(group, Z, c1, num_c1, c2, &Q1, &u1);
group->meth->add(group, out, &Q0, &Q1); // R = Q0 + Q1
// All our curves have cofactor one, so |clear_cofactor| is a no-op.
return 1;
}
static int felem_from_u8(const EC_GROUP *group, EC_FELEM *out, uint8_t a) {
uint8_t bytes[EC_MAX_BYTES] = {0};
size_t len = BN_num_bytes(&group->field.N);
bytes[len - 1] = a;
return ec_felem_from_bytes(group, out, bytes, len);
}
// kP256Sqrt10 is sqrt(10) in P-256's field. It was computed as follows in
// python3:
//
// p = 2**256 - 2**224 + 2**192 + 2**96 - 1
// c2 = pow(10, (p+1)//4, p)
// assert pow(c2, 2, p) == 10
// ", ".join("0x%02x" % b for b in c2.to_bytes(256//8, 'big'))
static const uint8_t kP256Sqrt10[] = {
0xda, 0x53, 0x8e, 0x3b, 0xe1, 0xd8, 0x9b, 0x99, 0xc9, 0x78, 0xfc,
0x67, 0x51, 0x80, 0xaa, 0xb2, 0x7b, 0x8d, 0x1f, 0xf8, 0x4c, 0x55,
0xd5, 0xb6, 0x2c, 0xcd, 0x34, 0x27, 0xe4, 0x33, 0xc4, 0x7f};
// kP384Sqrt12 is sqrt(12) in P-384's field. It was computed as follows in
// python3:
//
// p = 2**384 - 2**128 - 2**96 + 2**32 - 1
// c2 = pow(12, (p+1)//4, p)
// assert pow(c2, 2, p) == 12
// ", ".join("0x%02x" % b for b in c2.to_bytes(384//8, 'big'))
static const uint8_t kP384Sqrt12[] = {
0x2a, 0xcc, 0xb4, 0xa6, 0x56, 0xb0, 0x24, 0x9c, 0x71, 0xf0, 0x50, 0x0e,
0x83, 0xda, 0x2f, 0xdd, 0x7f, 0x98, 0xe3, 0x83, 0xd6, 0x8b, 0x53, 0x87,
0x1f, 0x87, 0x2f, 0xcb, 0x9c, 0xcb, 0x80, 0xc5, 0x3c, 0x0d, 0xe1, 0xf8,
0xa8, 0x0f, 0x7e, 0x19, 0x14, 0xe2, 0xec, 0x69, 0xf5, 0xa6, 0x26, 0xb3};
int ec_hash_to_curve_p256_xmd_sha256_sswu(const EC_GROUP *group,
EC_JACOBIAN *out, const uint8_t *dst,
size_t dst_len, const uint8_t *msg,
size_t msg_len) {
// See section 8.3 of RFC 9380.
if (EC_GROUP_get_curve_name(group) != NID_X9_62_prime256v1) {
OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
return 0;
}
// Z = -10, c2 = sqrt(10)
EC_FELEM Z, c2;
if (!felem_from_u8(group, &Z, 10) ||
!ec_felem_from_bytes(group, &c2, kP256Sqrt10, sizeof(kP256Sqrt10))) {
return 0;
}
ec_felem_neg(group, &Z, &Z);
return hash_to_curve(group, EVP_sha256(), &Z, &c2, /*k=*/128, out, dst,
dst_len, msg, msg_len);
}
int EC_hash_to_curve_p256_xmd_sha256_sswu(const EC_GROUP *group, EC_POINT *out,
const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len) {
if (EC_GROUP_cmp(group, out->group, NULL) != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
return 0;
}
return ec_hash_to_curve_p256_xmd_sha256_sswu(group, &out->raw, dst, dst_len,
msg, msg_len);
}
int ec_hash_to_curve_p384_xmd_sha384_sswu(const EC_GROUP *group,
EC_JACOBIAN *out, const uint8_t *dst,
size_t dst_len, const uint8_t *msg,
size_t msg_len) {
// See section 8.3 of RFC 9380.
if (EC_GROUP_get_curve_name(group) != NID_secp384r1) {
OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
return 0;
}
// Z = -12, c2 = sqrt(12)
EC_FELEM Z, c2;
if (!felem_from_u8(group, &Z, 12) ||
!ec_felem_from_bytes(group, &c2, kP384Sqrt12, sizeof(kP384Sqrt12))) {
return 0;
}
ec_felem_neg(group, &Z, &Z);
return hash_to_curve(group, EVP_sha384(), &Z, &c2, /*k=*/192, out, dst,
dst_len, msg, msg_len);
}
int EC_hash_to_curve_p384_xmd_sha384_sswu(const EC_GROUP *group, EC_POINT *out,
const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len) {
if (EC_GROUP_cmp(group, out->group, NULL) != 0) {
OPENSSL_PUT_ERROR(EC, EC_R_INCOMPATIBLE_OBJECTS);
return 0;
}
return ec_hash_to_curve_p384_xmd_sha384_sswu(group, &out->raw, dst, dst_len,
msg, msg_len);
}
int ec_hash_to_scalar_p384_xmd_sha384(
const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len) {
if (EC_GROUP_get_curve_name(group) != NID_secp384r1) {
OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
return 0;
}
return hash_to_scalar(group, EVP_sha384(), out, dst, dst_len, /*k=*/192, msg,
msg_len);
}
int ec_hash_to_curve_p384_xmd_sha512_sswu_draft07(
const EC_GROUP *group, EC_JACOBIAN *out, const uint8_t *dst,
size_t dst_len, const uint8_t *msg, size_t msg_len) {
// See section 8.3 of draft-irtf-cfrg-hash-to-curve-07.
if (EC_GROUP_get_curve_name(group) != NID_secp384r1) {
OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
return 0;
}
// Z = -12, c2 = sqrt(12)
EC_FELEM Z, c2;
if (!felem_from_u8(group, &Z, 12) ||
!ec_felem_from_bytes(group, &c2, kP384Sqrt12, sizeof(kP384Sqrt12))) {
return 0;
}
ec_felem_neg(group, &Z, &Z);
return hash_to_curve(group, EVP_sha512(), &Z, &c2, /*k=*/192, out, dst,
dst_len, msg, msg_len);
}
int ec_hash_to_scalar_p384_xmd_sha512_draft07(
const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len) {
if (EC_GROUP_get_curve_name(group) != NID_secp384r1) {
OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
return 0;
}
return hash_to_scalar(group, EVP_sha512(), out, dst, dst_len, /*k=*/192, msg,
msg_len);
}

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2020, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef OPENSSL_HEADER_EC_EXTRA_INTERNAL_H
#define OPENSSL_HEADER_EC_EXTRA_INTERNAL_H
#include <openssl/ec.h>
#include "../fipsmodule/ec/internal.h"
#if defined(__cplusplus)
extern "C" {
#endif
// Hash-to-curve.
//
// Internal |EC_JACOBIAN| versions of the corresponding public APIs.
// ec_hash_to_curve_p256_xmd_sha256_sswu hashes |msg| to a point on |group| and
// writes the result to |out|, implementing the P256_XMD:SHA-256_SSWU_RO_ suite
// from RFC 9380. It returns one on success and zero on error.
OPENSSL_EXPORT int ec_hash_to_curve_p256_xmd_sha256_sswu(
const EC_GROUP *group, EC_JACOBIAN *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len);
// ec_hash_to_curve_p384_xmd_sha384_sswu hashes |msg| to a point on |group| and
// writes the result to |out|, implementing the P384_XMD:SHA-384_SSWU_RO_ suite
// from RFC 9380. It returns one on success and zero on error.
OPENSSL_EXPORT int ec_hash_to_curve_p384_xmd_sha384_sswu(
const EC_GROUP *group, EC_JACOBIAN *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len);
// ec_hash_to_scalar_p384_xmd_sha384 hashes |msg| to a scalar on |group|
// and writes the result to |out|, using the hash_to_field operation from the
// P384_XMD:SHA-384_SSWU_RO_ suite from RFC 9380, but generating a value modulo
// the group order rather than a field element.
OPENSSL_EXPORT int ec_hash_to_scalar_p384_xmd_sha384(
const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len);
// ec_hash_to_curve_p384_xmd_sha512_sswu_draft07 hashes |msg| to a point on
// |group| and writes the result to |out|, implementing the
// P384_XMD:SHA-512_SSWU_RO_ suite from draft-irtf-cfrg-hash-to-curve-07. It
// returns one on success and zero on error.
//
// TODO(https://crbug.com/1414562): Migrate this to the final version.
OPENSSL_EXPORT int ec_hash_to_curve_p384_xmd_sha512_sswu_draft07(
const EC_GROUP *group, EC_JACOBIAN *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len);
// ec_hash_to_scalar_p384_xmd_sha512_draft07 hashes |msg| to a scalar on |group|
// and writes the result to |out|, using the hash_to_field operation from the
// P384_XMD:SHA-512_SSWU_RO_ suite from draft-irtf-cfrg-hash-to-curve-07, but
// generating a value modulo the group order rather than a field element.
//
// TODO(https://crbug.com/1414562): Migrate this to the final version.
OPENSSL_EXPORT int ec_hash_to_scalar_p384_xmd_sha512_draft07(
const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len);
enum ECParametersType {
UNKNOWN_EC_PARAMETERS = 0,
NAMED_CURVE_EC_PARAMETERS = 1,
SPECIFIED_CURVE_EC_PARAMETERS = 2,
};
// EC_KEY_parse_parameters_and_type parses the elliptic curve key parameters from |cbs| and
// sets the type of parameters found on |paramType|.
EC_GROUP *EC_KEY_parse_parameters_and_type(CBS *cbs, enum ECParametersType *paramType);
#if defined(__cplusplus)
} // extern C
#endif
#endif // OPENSSL_HEADER_EC_EXTRA_INTERNAL_H