712 lines
22 KiB
C
712 lines
22 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifdef FREEBL_NO_DEPEND
|
|
#include "stubs.h"
|
|
#endif
|
|
|
|
#include "blapi.h"
|
|
#include "blapii.h"
|
|
#include "prerr.h"
|
|
#include "secerr.h"
|
|
#include "secmpi.h"
|
|
#include "secitem.h"
|
|
#include "mplogic.h"
|
|
#include "ec.h"
|
|
#include "ecl.h"
|
|
#include "verified/Hacl_P384.h"
|
|
#include "verified/Hacl_P521.h"
|
|
#include "secport.h"
|
|
#include "verified/Hacl_Ed25519.h"
|
|
|
|
#define EC_DOUBLECHECK PR_FALSE
|
|
|
|
SECStatus
|
|
ec_ED25519_pt_validate(const SECItem *px)
|
|
{
|
|
if (!px || !px->data || px->len != Ed25519_PUBLIC_KEYLEN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
ec_ED25519_scalar_validate(const SECItem *scalar)
|
|
{
|
|
if (!scalar || !scalar->data || scalar->len != Ed25519_PRIVATE_KEYLEN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
static const ECMethod kMethods[] = {
|
|
{ ECCurve25519,
|
|
ec_Curve25519_pt_mul,
|
|
ec_Curve25519_pt_validate,
|
|
ec_Curve25519_scalar_validate,
|
|
NULL,
|
|
NULL },
|
|
{
|
|
ECCurve_NIST_P256,
|
|
ec_secp256r1_pt_mul,
|
|
ec_secp256r1_pt_validate,
|
|
ec_secp256r1_scalar_validate,
|
|
ec_secp256r1_sign_digest,
|
|
ec_secp256r1_verify_digest,
|
|
},
|
|
{
|
|
ECCurve_NIST_P384,
|
|
ec_secp384r1_pt_mul,
|
|
ec_secp384r1_pt_validate,
|
|
ec_secp384r1_scalar_validate,
|
|
ec_secp384r1_sign_digest,
|
|
ec_secp384r1_verify_digest,
|
|
},
|
|
{
|
|
ECCurve_NIST_P521,
|
|
ec_secp521r1_pt_mul,
|
|
ec_secp521r1_pt_validate,
|
|
ec_secp521r1_scalar_validate,
|
|
ec_secp521r1_sign_digest,
|
|
ec_secp521r1_verify_digest,
|
|
},
|
|
{ ECCurve_Ed25519,
|
|
NULL,
|
|
ec_ED25519_pt_validate,
|
|
ec_ED25519_scalar_validate,
|
|
NULL,
|
|
NULL },
|
|
};
|
|
|
|
static const ECMethod *
|
|
ec_get_method_from_name(ECCurveName name)
|
|
{
|
|
unsigned long i;
|
|
for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); ++i) {
|
|
if (kMethods[i].name == name) {
|
|
return &kMethods[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Generates a new EC key pair. The private key is a supplied
|
|
* value and the public key is the result of performing a scalar
|
|
* point multiplication of that value with the curve's base point.
|
|
*/
|
|
SECStatus
|
|
ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey,
|
|
const unsigned char *privKeyBytes, int privKeyLen)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
PLArenaPool *arena;
|
|
ECPrivateKey *key;
|
|
int len;
|
|
|
|
if (!ecParams || ecParams->name == ECCurve_noName ||
|
|
!privKey || !privKeyBytes || privKeyLen <= 0) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ecParams->fieldID.type != ec_field_plain) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Initialize an arena for the EC key. */
|
|
if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE)))
|
|
return SECFailure;
|
|
|
|
key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey));
|
|
if (!key) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Set the version number (SEC 1 section C.4 says it should be 1) */
|
|
SECITEM_AllocItem(arena, &key->version, 1);
|
|
key->version.data[0] = 1;
|
|
|
|
/* Copy all of the fields from the ECParams argument to the
|
|
* ECParams structure within the private key.
|
|
*/
|
|
key->ecParams.arena = arena;
|
|
key->ecParams.type = ecParams->type;
|
|
key->ecParams.fieldID.size = ecParams->fieldID.size;
|
|
key->ecParams.fieldID.type = ecParams->fieldID.type;
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.prime,
|
|
&ecParams->fieldID.u.prime));
|
|
key->ecParams.fieldID.k1 = ecParams->fieldID.k1;
|
|
key->ecParams.fieldID.k2 = ecParams->fieldID.k2;
|
|
key->ecParams.fieldID.k3 = ecParams->fieldID.k3;
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.a,
|
|
&ecParams->curve.a));
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.b,
|
|
&ecParams->curve.b));
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.seed,
|
|
&ecParams->curve.seed));
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.base,
|
|
&ecParams->base));
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.order,
|
|
&ecParams->order));
|
|
key->ecParams.cofactor = ecParams->cofactor;
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.DEREncoding,
|
|
&ecParams->DEREncoding));
|
|
key->ecParams.name = ecParams->name;
|
|
CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curveOID,
|
|
&ecParams->curveOID));
|
|
|
|
SECITEM_AllocItem(arena, &key->publicValue, EC_GetPointSize(ecParams));
|
|
len = ecParams->order.len;
|
|
SECITEM_AllocItem(arena, &key->privateValue, len);
|
|
|
|
/* Copy private key */
|
|
if (privKeyLen >= len) {
|
|
memcpy(key->privateValue.data, privKeyBytes, len);
|
|
} else {
|
|
memset(key->privateValue.data, 0, (len - privKeyLen));
|
|
memcpy(key->privateValue.data + (len - privKeyLen), privKeyBytes, privKeyLen);
|
|
}
|
|
|
|
/* Compute corresponding public key */
|
|
|
|
/* Use curve specific code for point multiplication */
|
|
if (ecParams->name == ECCurve_Ed25519) {
|
|
CHECK_SEC_OK(ED_DerivePublicKey(&key->privateValue, &key->publicValue));
|
|
} else {
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->pt_mul == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
rv = SECFailure;
|
|
goto cleanup;
|
|
}
|
|
CHECK_SEC_OK(method->pt_mul(&key->publicValue, &key->privateValue, NULL));
|
|
}
|
|
|
|
NSS_DECLASSIFY(key->publicValue.data, key->publicValue.len); /* Declassifying public key to avoid false positive */
|
|
*privKey = key;
|
|
return SECSuccess;
|
|
|
|
cleanup:
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
return rv;
|
|
}
|
|
|
|
/* Generates a new EC key pair. The private key is a supplied
|
|
* random value (in seed) and the public key is the result of
|
|
* performing a scalar point multiplication of that value with
|
|
* the curve's base point.
|
|
*/
|
|
SECStatus
|
|
EC_NewKeyFromSeed(ECParams *ecParams, ECPrivateKey **privKey,
|
|
const unsigned char *seed, int seedlen)
|
|
{
|
|
return ec_NewKey(ecParams, privKey, seed, seedlen);
|
|
}
|
|
|
|
/* Generate a random private key using the algorithm A.4.1 or A.4.2 of ANSI X9.62,
|
|
* modified a la FIPS 186-2 Change Notice 1 to eliminate the bias in the
|
|
* random number generator.
|
|
*/
|
|
|
|
SECStatus
|
|
ec_GenerateRandomPrivateKey(ECParams *ecParams, SECItem *privKey)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
|
|
unsigned int len = EC_GetScalarSize(ecParams);
|
|
|
|
if (privKey->len != len || privKey->data == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->scalar_validate == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
uint8_t leading_coeff_mask;
|
|
switch (ecParams->name) {
|
|
case ECCurve_Ed25519:
|
|
case ECCurve25519:
|
|
case ECCurve_NIST_P256:
|
|
case ECCurve_NIST_P384:
|
|
leading_coeff_mask = 0xff;
|
|
break;
|
|
case ECCurve_NIST_P521:
|
|
leading_coeff_mask = 0x01;
|
|
break;
|
|
default:
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* The rejection sampling method from FIPS 186-5 A.4.2 */
|
|
int count = 100;
|
|
do {
|
|
rv = RNG_GenerateGlobalRandomBytes(privKey->data, len);
|
|
if (rv != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_NEED_RANDOM);
|
|
return SECFailure;
|
|
}
|
|
privKey->data[0] &= leading_coeff_mask;
|
|
NSS_CLASSIFY(privKey->data, privKey->len);
|
|
rv = method->scalar_validate(privKey);
|
|
} while (rv != SECSuccess && --count > 0);
|
|
|
|
if (rv != SECSuccess) { // implies count == 0
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Generates a new EC key pair. The private key is a random value and
|
|
* the public key is the result of performing a scalar point multiplication
|
|
* of that value with the curve's base point.
|
|
*/
|
|
SECStatus
|
|
EC_NewKey(ECParams *ecParams, ECPrivateKey **privKey)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
SECItem privKeyRand = { siBuffer, NULL, 0 };
|
|
|
|
if (!ecParams || ecParams->name == ECCurve_noName || !privKey) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECITEM_AllocItem(NULL, &privKeyRand, EC_GetScalarSize(ecParams));
|
|
if (privKeyRand.data == NULL) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
rv = SECFailure;
|
|
goto cleanup;
|
|
}
|
|
rv = ec_GenerateRandomPrivateKey(ecParams, &privKeyRand);
|
|
if (rv != SECSuccess || privKeyRand.data == NULL) {
|
|
goto cleanup;
|
|
}
|
|
/* generate public key */
|
|
CHECK_SEC_OK(ec_NewKey(ecParams, privKey, privKeyRand.data, privKeyRand.len));
|
|
|
|
cleanup:
|
|
if (privKeyRand.data) {
|
|
SECITEM_ZfreeItem(&privKeyRand, PR_FALSE);
|
|
}
|
|
#if EC_DEBUG
|
|
printf("EC_NewKey returning %s\n",
|
|
(rv == SECSuccess) ? "success" : "failure");
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Validates an EC public key as described in Section 5.2.2 of
|
|
* X9.62. The ECDH primitive when used without the cofactor does
|
|
* not address small subgroup attacks, which may occur when the
|
|
* public key is not valid. These attacks can be prevented by
|
|
* validating the public key before using ECDH.
|
|
*/
|
|
SECStatus
|
|
EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue)
|
|
{
|
|
if (!ecParams || ecParams->name == ECCurve_noName ||
|
|
!publicValue || !publicValue->len) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Uses curve specific code for point validation. */
|
|
if (ecParams->fieldID.type != ec_field_plain) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->pt_validate == NULL) {
|
|
/* unknown curve */
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus rv = method->pt_validate(publicValue);
|
|
if (rv != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
** Performs an ECDH key derivation by computing the scalar point
|
|
** multiplication of privateValue and publicValue (with or without the
|
|
** cofactor) and returns the x-coordinate of the resulting elliptic
|
|
** curve point in derived secret. If successful, derivedSecret->data
|
|
** is set to the address of the newly allocated buffer containing the
|
|
** derived secret, and derivedSecret->len is the size of the secret
|
|
** produced. It is the caller's responsibility to free the allocated
|
|
** buffer containing the derived secret.
|
|
*/
|
|
SECStatus
|
|
ECDH_Derive(SECItem *publicValue,
|
|
ECParams *ecParams,
|
|
SECItem *privateValue,
|
|
PRBool withCofactor,
|
|
SECItem *derivedSecret)
|
|
{
|
|
if (!publicValue || !publicValue->len ||
|
|
!ecParams || ecParams->name == ECCurve_noName ||
|
|
!privateValue || !privateValue->len || !derivedSecret) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* Make sure the point is on the requested curve to avoid
|
|
* certain small subgroup attacks.
|
|
*/
|
|
if (EC_ValidatePublicKey(ecParams, publicValue) != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Perform curve specific multiplication using ECMethod */
|
|
if (ecParams->fieldID.type != ec_field_plain) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->pt_validate == NULL ||
|
|
method->pt_mul == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
memset(derivedSecret, 0, sizeof(*derivedSecret));
|
|
derivedSecret = SECITEM_AllocItem(NULL, derivedSecret, EC_GetScalarSize(ecParams));
|
|
if (derivedSecret == NULL) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus rv = method->pt_mul(derivedSecret, privateValue, publicValue);
|
|
if (rv != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
SECITEM_ZfreeItem(derivedSecret, PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/* Computes the ECDSA signature (a concatenation of two values r and s)
|
|
* on the digest using the given key and the random value kb (used in
|
|
* computing s).
|
|
*/
|
|
|
|
static SECStatus
|
|
ec_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature,
|
|
const SECItem *digest, const unsigned char *kb, const int kblen)
|
|
{
|
|
ECParams *ecParams = NULL;
|
|
unsigned olen; /* length in bytes of the base point order */
|
|
|
|
/* Check args */
|
|
if (!key || !signature || !digest || !kb || (kblen <= 0)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
ecParams = &(key->ecParams);
|
|
olen = ecParams->order.len;
|
|
if (signature->data == NULL) {
|
|
/* a call to get the signature length only */
|
|
signature->len = 2 * olen;
|
|
return SECSuccess;
|
|
}
|
|
if (signature->len < 2 * olen) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Perform curve specific signature using ECMethod */
|
|
if (ecParams->fieldID.type != ec_field_plain) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->sign_digest == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus rv = method->sign_digest(key, signature, digest, kb, kblen);
|
|
if (rv != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
}
|
|
|
|
#if EC_DEBUG
|
|
printf("ECDSA signing with seed %s\n",
|
|
(rv == SECSuccess) ? "succeeded" : "failed");
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature,
|
|
const SECItem *digest, const unsigned char *kb, const int kblen)
|
|
{
|
|
#if EC_DEBUG || EC_DOUBLECHECK
|
|
SECItem *signature2 = SECITEM_AllocItem(NULL, NULL, signature->len);
|
|
SECStatus signSuccess = ec_SignDigestWithSeed(key, signature, digest, kb, kblen);
|
|
SECStatus signSuccessDouble = ec_SignDigestWithSeed(key, signature2, digest, kb, kblen);
|
|
int signaturesEqual = NSS_SecureMemcmp(signature->data, signature2->data, signature->len);
|
|
SECStatus rv;
|
|
|
|
if ((signaturesEqual == 0) && (signSuccess == SECSuccess) && (signSuccessDouble == SECSuccess)) {
|
|
rv = SECSuccess;
|
|
} else {
|
|
rv = SECFailure;
|
|
}
|
|
|
|
#if EC_DEBUG
|
|
printf("ECDSA signing with seed %s after signing twice\n", (rv == SECSuccess) ? "succeeded" : "failed");
|
|
#endif
|
|
|
|
SECITEM_FreeItem(signature2, PR_TRUE);
|
|
return rv;
|
|
#else
|
|
return ec_SignDigestWithSeed(key, signature, digest, kb, kblen);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
** Computes the ECDSA signature on the digest using the given key
|
|
** and a random seed.
|
|
*/
|
|
SECStatus
|
|
ECDSA_SignDigest(ECPrivateKey *key, SECItem *signature, const SECItem *digest)
|
|
{
|
|
SECItem nonceRand = { siBuffer, NULL, 0 };
|
|
|
|
if (!key) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Generate random value k */
|
|
SECITEM_AllocItem(NULL, &nonceRand, EC_GetScalarSize(&key->ecParams));
|
|
if (nonceRand.data == NULL) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus rv = ec_GenerateRandomPrivateKey(&key->ecParams, &nonceRand);
|
|
if (rv != SECSuccess) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Generate ECDSA signature with the specified k value */
|
|
rv = ECDSA_SignDigestWithSeed(key, signature, digest, nonceRand.data, nonceRand.len);
|
|
NSS_DECLASSIFY(signature->data, signature->len);
|
|
|
|
cleanup:
|
|
SECITEM_ZfreeItem(&nonceRand, PR_FALSE);
|
|
|
|
#if EC_DEBUG
|
|
printf("ECDSA signing %s\n",
|
|
(rv == SECSuccess) ? "succeeded" : "failed");
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
** Checks the signature on the given digest using the key provided.
|
|
**
|
|
** The key argument must represent a valid EC public key (a point on
|
|
** the relevant curve). If it is not a valid point, then the behavior
|
|
** of this function is undefined. In cases where a public key might
|
|
** not be valid, use EC_ValidatePublicKey to check.
|
|
*/
|
|
SECStatus
|
|
ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature,
|
|
const SECItem *digest)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
ECParams *ecParams = NULL;
|
|
|
|
/* Check args */
|
|
if (!key || !signature || !digest) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
ecParams = &(key->ecParams);
|
|
|
|
/* Perform curve specific signature verification using ECMethod */
|
|
if (ecParams->fieldID.type != ec_field_plain) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->verify_digest == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
rv = method->verify_digest(key, signature, digest);
|
|
if (rv != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
|
|
}
|
|
|
|
#if EC_DEBUG
|
|
printf("ECDSA verification %s\n",
|
|
(rv == SECSuccess) ? "succeeded" : "failed");
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*EdDSA: Currently only Ed22519 is implemented.*/
|
|
|
|
/*
|
|
** Computes the EdDSA signature on the message using the given key.
|
|
*/
|
|
|
|
SECStatus
|
|
ec_ED25519_public_key_validate(const ECPublicKey *key)
|
|
{
|
|
if (!key || !(key->ecParams.name == ECCurve_Ed25519)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
return ec_ED25519_pt_validate(&key->publicValue);
|
|
}
|
|
|
|
SECStatus
|
|
ec_ED25519_private_key_validate(const ECPrivateKey *key)
|
|
{
|
|
if (!key || !(key->ecParams.name == ECCurve_Ed25519)) {
|
|
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
return ec_ED25519_scalar_validate(&key->privateValue);
|
|
}
|
|
|
|
SECStatus
|
|
ED_SignMessage(ECPrivateKey *key, SECItem *signature, const SECItem *msg)
|
|
{
|
|
if (!msg || !signature || signature->len != Ed25519_SIGN_LEN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ec_ED25519_private_key_validate(key) != SECSuccess) {
|
|
return SECFailure; /* error code set by ec_ED25519_scalar_validate. */
|
|
}
|
|
|
|
if (signature->data) {
|
|
Hacl_Ed25519_sign(signature->data, key->privateValue.data, msg->len,
|
|
msg->data);
|
|
}
|
|
signature->len = ED25519_SIGN_LEN;
|
|
BLAPI_CLEAR_STACK(2048);
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
** Checks the signature on the given message using the key provided.
|
|
*/
|
|
|
|
SECStatus
|
|
ED_VerifyMessage(ECPublicKey *key, const SECItem *signature,
|
|
const SECItem *msg)
|
|
{
|
|
if (!msg || !signature || !signature->data || signature->len != Ed25519_SIGN_LEN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ec_ED25519_public_key_validate(key) != SECSuccess) {
|
|
return SECFailure; /* error code set by ec_ED25519_pt_validate. */
|
|
}
|
|
|
|
bool rv = Hacl_Ed25519_verify(key->publicValue.data, msg->len, msg->data,
|
|
signature->data);
|
|
BLAPI_CLEAR_STACK(2048);
|
|
|
|
#if EC_DEBUG
|
|
printf("ED_VerifyMessage returning %s\n",
|
|
(rv) ? "success" : "failure");
|
|
#endif
|
|
|
|
if (rv) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus
|
|
ED_DerivePublicKey(const SECItem *privateKey, SECItem *publicKey)
|
|
{
|
|
/* Currently supporting only Ed25519.*/
|
|
if (!privateKey || privateKey->len == 0 || !publicKey || publicKey->len != Ed25519_PUBLIC_KEYLEN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (ec_ED25519_scalar_validate(privateKey) != SECSuccess) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
Hacl_Ed25519_secret_to_public(publicKey->data, privateKey->data);
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
X25519_DerivePublicKey(const SECItem *privateKey, SECItem *publicKey)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
/* Currently supporting only X25519.*/
|
|
if (!privateKey || privateKey->len == 0 || !publicKey || publicKey->len != X25519_PUBLIC_KEYLEN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ECCurve25519);
|
|
if (method == NULL || method->pt_mul == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
rv = method->pt_mul(publicKey, (SECItem *)privateKey, NULL);
|
|
return rv;
|
|
}
|
|
|
|
SECStatus
|
|
EC_DerivePublicKey(const SECItem *privateKey, const ECParams *ecParams, SECItem *publicKey)
|
|
{
|
|
if (!privateKey || privateKey->len == 0 || !publicKey || publicKey->len != EC_GetPointSize(ecParams)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
const ECMethod *method = ec_get_method_from_name(ecParams->name);
|
|
if (method == NULL || method->pt_mul == NULL) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
|
|
return SECFailure;
|
|
}
|
|
|
|
return method->pt_mul(publicKey, (SECItem *)privateKey, NULL);
|
|
}
|