293 lines
7.7 KiB
C
293 lines
7.7 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 "ecl-priv.h"
|
|
#include "secitem.h"
|
|
#include "secerr.h"
|
|
#include "secmpi.h"
|
|
#include "../verified/Hacl_P521.h"
|
|
|
|
/*
|
|
* Point Validation for P-521.
|
|
*/
|
|
|
|
SECStatus
|
|
ec_secp521r1_pt_validate(const SECItem *pt)
|
|
{
|
|
SECStatus res = SECSuccess;
|
|
if (!pt || !pt->data) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
if (pt->len != 133) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
if (pt->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
bool b = Hacl_P521_validate_public_key(pt->data + 1);
|
|
|
|
if (!b) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Scalar Validation for P-521.
|
|
*/
|
|
|
|
SECStatus
|
|
ec_secp521r1_scalar_validate(const SECItem *scalar)
|
|
{
|
|
SECStatus res = SECSuccess;
|
|
if (!scalar || !scalar->data) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
if (scalar->len != 66) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
bool b = Hacl_P521_validate_private_key(scalar->data);
|
|
|
|
if (!b) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Scalar multiplication for P-521.
|
|
* If P == NULL, the base point is used.
|
|
* Returns X = k*P
|
|
*/
|
|
|
|
SECStatus
|
|
ec_secp521r1_pt_mul(SECItem *X, SECItem *k, SECItem *P)
|
|
{
|
|
SECStatus res = SECSuccess;
|
|
if (!P) {
|
|
uint8_t derived[132] = { 0 };
|
|
|
|
if (!X || !k || !X->data || !k->data ||
|
|
X->len < 133 || k->len != 66) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
bool b = Hacl_P521_dh_initiator(derived, k->data);
|
|
|
|
if (!b) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
X->len = 133;
|
|
X->data[0] = EC_POINT_FORM_UNCOMPRESSED;
|
|
memcpy(X->data + 1, derived, 132);
|
|
|
|
} else {
|
|
uint8_t full_key[66] = { 0 };
|
|
uint8_t *key;
|
|
uint8_t derived[132] = { 0 };
|
|
|
|
if (!X || !k || !P || !X->data || !k->data || !P->data ||
|
|
X->len < 66 || P->len != 133 ||
|
|
P->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
/* We consider keys of up to size 66, or of size 67 with a single leading 0 */
|
|
if (k->len < 66) {
|
|
memcpy(full_key + 66 - k->len, k->data, k->len);
|
|
key = full_key;
|
|
} else if (k->len == 66) {
|
|
key = k->data;
|
|
} else if (k->len == 67 && k->data[0] == 0) {
|
|
key = k->data + 1;
|
|
} else {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
bool b = Hacl_P521_dh_responder(derived, P->data + 1, key);
|
|
|
|
if (!b) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
X->len = 66;
|
|
memcpy(X->data, derived, 66);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* ECDSA Signature for P-521
|
|
*/
|
|
|
|
SECStatus
|
|
ec_secp521r1_sign_digest(ECPrivateKey *ecPrivKey, SECItem *signature,
|
|
const SECItem *digest, const unsigned char *kb,
|
|
const unsigned int kblen)
|
|
{
|
|
SECStatus res = SECSuccess;
|
|
|
|
if (!ecPrivKey || !signature || !digest || !kb ||
|
|
!ecPrivKey->privateValue.data ||
|
|
!signature->data || !digest->data ||
|
|
ecPrivKey->ecParams.name != ECCurve_NIST_P521) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
|
|
if (kblen == 0 || digest->len == 0 || signature->len < 132) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
|
|
// Private keys should be 66 bytes, but some software trims leading zeros,
|
|
// and some software produces 67 byte keys with a leading zero. We'll
|
|
// accept these variants.
|
|
uint8_t padded_key_data[66] = { 0 };
|
|
uint8_t *key;
|
|
SECItem *privKey = &ecPrivKey->privateValue;
|
|
if (privKey->len == 66) {
|
|
key = privKey->data;
|
|
} else if (privKey->len == 67 && privKey->data[0] == 0) {
|
|
key = privKey->data + 1;
|
|
} else if (privKey->len < 66) {
|
|
memcpy(padded_key_data + 66 - privKey->len, privKey->data, privKey->len);
|
|
key = padded_key_data;
|
|
} else {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
|
|
uint8_t hash[66] = { 0 };
|
|
if (digest->len < 66) {
|
|
memcpy(hash + 66 - digest->len, digest->data, digest->len);
|
|
} else {
|
|
// SEC 1 takes the most significant ceil(log(n)) bits of hash output when the hash output is longer than log(n).
|
|
hash[0] = digest->data[0] >> 7;
|
|
for (size_t i = 1; i < 66; i++) {
|
|
hash[i] = (digest->data[i - 1] << 1) | (digest->data[i] >> 7);
|
|
}
|
|
}
|
|
|
|
uint8_t nonce[66] = { 0 };
|
|
if (kblen < 66) {
|
|
memcpy(nonce + 66 - kblen, kb, kblen);
|
|
} else {
|
|
memcpy(nonce, kb, 66);
|
|
}
|
|
|
|
bool b = Hacl_P521_ecdsa_sign_p521_without_hash(
|
|
signature->data, 66, hash, key, nonce);
|
|
if (!b) {
|
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
signature->len = 132;
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* ECDSA Signature Verification for P-521
|
|
*/
|
|
|
|
SECStatus
|
|
ec_secp521r1_verify_digest(ECPublicKey *key, const SECItem *signature,
|
|
const SECItem *digest)
|
|
{
|
|
SECStatus res = SECSuccess;
|
|
|
|
if (!key || !signature || !digest ||
|
|
!key->publicValue.data ||
|
|
!signature->data || !digest->data ||
|
|
key->ecParams.name != ECCurve_NIST_P521) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
if (signature->len == 0 || signature->len % 2 != 0 ||
|
|
signature->len > 132 || digest->len == 0 ||
|
|
key->publicValue.len != 133) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
if (key->publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
|
|
PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
// Signatures should be 132 bytes, but some software produces short signatures.
|
|
// Pad components with zeros if necessary.
|
|
uint8_t paddedSigData[132] = { 0 };
|
|
uint8_t *sig;
|
|
if (signature->len != 132) {
|
|
size_t split = signature->len / 2;
|
|
|
|
memcpy(paddedSigData + 66 - split, signature->data, split);
|
|
memcpy(paddedSigData + 132 - split, signature->data + split, split);
|
|
|
|
sig = paddedSigData;
|
|
} else {
|
|
sig = signature->data;
|
|
}
|
|
|
|
uint8_t hash[66] = { 0 };
|
|
if (digest->len < 66) {
|
|
memcpy(hash + 66 - digest->len, digest->data, digest->len);
|
|
} else {
|
|
// SEC 1 takes the most significant ceil(log(n)) bits of hash output when the hash output is longer than log(n).
|
|
hash[0] = digest->data[0] >> 7;
|
|
for (size_t i = 1; i < 66; i++) {
|
|
hash[i] = (digest->data[i - 1] << 1) | (digest->data[i] >> 7);
|
|
}
|
|
}
|
|
|
|
bool b = Hacl_P521_ecdsa_verif_without_hash(
|
|
66, hash, key->publicValue.data + 1, sig, sig + 66);
|
|
if (!b) {
|
|
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
|
|
res = SECFailure;
|
|
return res;
|
|
}
|
|
|
|
return res;
|
|
}
|