Files
mars-matrixssl/crypto/pubkey/ecc.c
2016-05-03 17:36:14 -07:00

2691 lines
71 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file ecc.c
* @version $Format:%h%d$
*
* Implements ECC over Z/pZ for curve y^2 = x^3 + ax + b.
*/
/*
* Copyright (c) 2013-2016 INSIDE Secure Corporation
* Copyright (c) PeerSec Networks, 2002-2011
* All Rights Reserved
*
* The latest version of this code is available at http://www.matrixssl.org
*
* This software is open source; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This General Public License does NOT permit incorporating this software
* into proprietary programs. If you are unable to comply with the GPL, a
* commercial license for this software may be purchased from INSIDE at
* http://www.insidesecure.com/
*
* This program is distributed in WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* http://www.gnu.org/copyleft/gpl.html
*/
/******************************************************************************/
#include "../cryptoApi.h"
#ifdef USE_MATRIX_ECC
/******************************************************************************/
#define ECC_BUF_SIZE 256
static psEccPoint_t *eccNewPoint(psPool_t *pool, short size);
static void eccFreePoint(psEccPoint_t *p);
static int32_t eccMulmod(psPool_t *pool, const pstm_int *k, const psEccPoint_t *G,
psEccPoint_t *R, pstm_int *modulus, uint8_t map, pstm_int *tmp_int);
static int32_t eccProjectiveAddPoint(psPool_t *pool, const psEccPoint_t *P,
const psEccPoint_t *Q, psEccPoint_t *R, const pstm_int *modulus,
const pstm_digit *mp, pstm_int *tmp_int);
static int32_t eccProjectiveDblPoint(psPool_t *pool, const psEccPoint_t *P,
psEccPoint_t *R, const pstm_int *modulus, const pstm_digit *mp,
const pstm_int *A);
static int32_t eccMap(psPool_t *pool, psEccPoint_t *P, const pstm_int *modulus,
const pstm_digit *mp);
/*
This array holds the ecc curve settings.
The recommended elliptic curve domain parameters over p have been given
nicknames to enable them to be easily identified. The nicknames were
chosen as follows. Each name begins with sec to denote Standards for
Efficient Cryptography, followed by a p to denote parameters over p,
followed by a number denoting the length in bits of the field size p,
followed by a k to denote parameters associated with a Koblitz curve or an
r to denote verifiably random parameters, followed by a sequence number.
typedef struct {
uint8_t size; // The size of the curve in octets
uint16_t curveId; // IANA named curve id for TLS use
uint8_t isOptimized; // 1 if optimized with field parameter A=-3
uint32_t OIDsum; // Internal Matrix OID
//Domain parameters
const char *name; // name of curve
const char *prime; // prime defining the field the curve is in (hex)
const char *A; // The fields A param (hex)
const char *B; // The fields B param (hex)
const char *order; // The order of the curve (hex)
const char *Gx; // The x co-ordinate of the base point on the curve (hex)
const char *Gy; // The y co-ordinate of the base point on the curve (hex)
} psEccCurve_t;
*/
const static psEccCurve_t eccCurve[] = {
#ifdef USE_SECP521R1
{
66,
IANA_SECP521R1,
1, /* isOptimized */
211, /* 43.129.4.0.35 */
"secp521r1",
"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
"51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
"C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
"11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
},
#endif
#ifdef USE_BRAIN512R1
{
64, /* size in octets */
IANA_BRAIN512R1,
0, /* isOptimized */
110, /* 1.3.36.3.3.2.8.1.1.13 */
"brainpoolP512r1",
"AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
"7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
"3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
"AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
"81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
"7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
},
#endif
#ifdef USE_SECP384R1
{
48,
IANA_SECP384R1,
1, /* isOptimized */
210, /* 43.129.4.0.34 */
"secp384r1",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
"3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
},
#endif
#ifdef USE_BRAIN384R1
{
48, /* size in octets */
IANA_BRAIN384R1,
0, /* isOptimized */
108, /* 1.3.36.3.3.2.8.1.1.11 */
"brainpoolP384r1",
"8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
"7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
"04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
"8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
"1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
"8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
},
#endif
#ifdef USE_SECP256R1
{
32,
IANA_SECP256R1,
1, /* isOptimized */
526, /* 42.134.72.206.61.3.1.7 */
"secp256r1",
"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
"5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
"6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
"4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
},
#endif
#ifdef USE_BRAIN256R1
{
32, /* size in octets */
IANA_BRAIN256R1,
0, /* isOptimized */
104, /* 1.3.36.3.3.2.8.1.1.7 */
"brainpoolP256r1",
"A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377",
"7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9",
"26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6",
"A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7",
"8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262",
"547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997",
},
#endif
#ifdef USE_SECP224R1
{
28,
IANA_SECP224R1,
1, /* isOptimized */
209, /* 43.129.4.0.33 */
"secp224r1",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE",
"B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
"B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
"BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
},
#endif
#ifdef USE_BRAIN224R1
{
28, /* size in octets */
IANA_BRAIN224R1,
0, /* isOptimized */
102, /* 1.3.36.3.3.2.8.1.1.5 */
"brainpoolP224r1",
"D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF",
"68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43",
"2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B",
"D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F",
"0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D",
"58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD"
},
#endif
#ifdef USE_SECP192R1
{
24, /* size in octets */
IANA_SECP192R1, /* IANA named curve ID */
1, /* isOptimized */
520, /* 42.134.72.206.61.3.1.1 */
"secp192r1",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", /* prime */
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", /* A = -3 */
"64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", /* B */
"FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", /* order */
"188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", /* Gx */
"07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", /* Gy */
},
#endif
{
0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
}
};
/*****************************************************************************/
/**
Initialize an ecc key, and assign the curve, if provided.
@param[in] pool Memory pool
@param[out] key Pointer to allocated ECC key to initialize
@param[in] curve Curve to assign, or NULL.
@return < 0 on failure, 0 on success.
@note To allocate and initialize a key, use psEccNewKey().
*/
int32_t psEccInitKey(psPool_t *pool, psEccKey_t *key, const psEccCurve_t *curve)
{
if (!key) {
return PS_MEM_FAIL;
}
memset(key, 0x0, sizeof(psEccKey_t));
key->pool = pool;
key->pubkey.pool = pool;
key->curve = curve; /* Curve can be NULL */
/* key->type will be set by one of the key generate/import/read functions */
return PS_SUCCESS;
}
/**
Clear an ECC key.
@param[out] key Pointer to allocated ECC key to clear.
@note Caller is responsible for freeing memory associated with key structure,
if appropriate.
*/
void psEccClearKey(psEccKey_t *key)
{
psAssert(key);
/* Clear private k separately, since it may not be present */
pstm_clear(&key->k);
pstm_clear_multi(
&key->pubkey.x,
&key->pubkey.y,
&key->pubkey.z,
NULL, NULL, NULL, NULL, NULL);
key->curve = NULL;
key->pool = NULL;
key->pubkey.pool = NULL;
key->type = 0;
}
/**
Allocate memory for an ECC key and initialize it.
@param[in] pool Memory pool
@param[out] key Pointer to unallocated ECC key to initialize. Will
point to allocated and initialized key on successful return.
@param[in] curve Curve to assign, or NULL.
@return < 0 on failure, 0 on success.
*/
int32_t psEccNewKey(psPool_t *pool, psEccKey_t **key, const psEccCurve_t *curve)
{
psEccKey_t *k;
int32_t rc;
if ((k = psMalloc(pool, sizeof(psEccKey_t))) == NULL) {
return PS_MEM_FAIL;
}
k->type = 0;
if ((rc = psEccInitKey(pool, k, curve)) < 0) {
psFree(k, pool);
return rc;
}
*key = k;
return PS_SUCCESS;
}
/* 'to' digits will be allocated here */
int32 psEccCopyKey(psEccKey_t *to, psEccKey_t *from)
{
int32 rc;
if (to->pool == NULL) {
to->pool = from->pool;
to->pubkey.pool = from->pubkey.pool;
} else {
to->pubkey.pool = to->pool;
}
to->curve = from->curve;
to->type = from->type;
/* pubkey */
if ((rc = pstm_init_copy(to->pool, &to->pubkey.x, &from->pubkey.x, 0))
!= PSTM_OKAY) {
goto error;
}
if ((rc = pstm_init_copy(to->pool, &to->pubkey.y, &from->pubkey.y, 0))
!= PSTM_OKAY) {
goto error;
}
if ((rc = pstm_init_copy(to->pool, &to->pubkey.z, &from->pubkey.z, 0))
!= PSTM_OKAY) {
goto error;
}
/* privkey */
if (to->type == PS_PRIVKEY) {
if ((rc = pstm_init_copy(to->pool, &to->k, &from->k, 0))
!= PSTM_OKAY) {
goto error;
}
}
error:
if (rc < 0) {
psEccClearKey(from);
}
return rc;
}
/**
Free memory for an ECC key and clear it.
@param[out] key Pointer to dynamically allocated ECC key to free. Pointer
will be cleared, freed and set to NULL on return.
*/
void psEccDeleteKey(psEccKey_t **key)
{
psEccKey_t *k = *key;
psEccClearKey(k);
psFree(k, NULL);
*key = NULL;
}
/**
ECC key size in bytes.
@return Public key size in bytes if key->type is public, otherwise private size.
@note ECC public keys are twice as many bytes as private keys.
*/
uint8_t psEccSize(const psEccKey_t *key)
{
if (key && key->curve) {
return key->curve->size * 2;
}
return 0;
}
/*****************************************************************************/
/*
Called from the cert parse. The initial bytes in this stream are
technically the EcpkParameters from the ECDSA pub key OBJECT IDENTIFIER
that name the curve. The asnGetAlgorithmIdentifier call right before
this just stripped out the OID
*/
int32_t getEcPubKey(psPool_t *pool, const unsigned char **pp, uint16_t len,
psEccKey_t *pubKey, unsigned char sha1KeyHash[SHA1_HASH_SIZE])
{
#ifdef USE_SHA1
psDigestContext_t dc;
#endif
const psEccCurve_t *eccCurve;
const unsigned char *p = *pp, *end;
int32_t oid;
uint16_t arcLen;
uint8_t ignore_bits;
end = p + len;
if (len < 1 ||
*(p++) != ASN_OID ||
getAsnLength(&p, (uint16_t)(end - p), &arcLen) < 0 ||
(uint16_t)(end - p) < arcLen) {
psTraceCrypto("Only namedCurve types are supported in EC certs\n");
return -1;
}
/*
NamedCurve OIDs
ansi-x9-62 OBJECT IDENTIFER ::= {
iso(1) member-body(2) us(840) 10045
}
secp192r1 OBJECT IDENTIFIER ::= { ansi-x9-62 curves(3) prime(1) 1 }
2a8648ce3d030101 -> sum = 520
secp256r1 OBJECT IDENTIFIER ::= { ansi-x9-62 curves(3) prime(1) 7 }
2a8648ce3d030107 -> sum = 526
*/
/* Note arcLen could be zero here */
oid = 0;
while (arcLen > 0) {
oid += *p++;
arcLen--;
}
/* Match the sum against our list of curves to make sure we got it */
if (getEccParamByOid(oid, &eccCurve) < 0) {
psTraceCrypto("Cert named curve not found in eccCurve list\n");
return -1;
}
if ((uint16_t)(end - p) < 1 || (*(p++) != ASN_BIT_STRING) ||
getAsnLength(&p, len - 1, &arcLen) < 0 ||
(uint16_t)(end - p) < arcLen ||
arcLen < 1) {
psTraceCrypto("Unexpected ECC pubkey format\n");
return -1;
}
ignore_bits = *p++;
arcLen--;
if (ignore_bits != 0) {
psTraceCrypto("Unexpected ECC pubkey format\n");
}
#ifdef USE_SHA1
/* A public key hash is used in PKI tools (OCSP, Trusted CA indication).
Standard form - SHA-1 hash of the value of the BIT STRING
subjectPublicKey [excluding the tag, length, and number of unused
bits] */
psSha1Init(&dc.sha1);
psSha1Update(&dc.sha1, p, arcLen);
psSha1Final(&dc.sha1, sha1KeyHash);
#endif
/* Note arcLen could again be zero here */
if (psEccX963ImportKey(pool, p, arcLen, pubKey, eccCurve) < 0) {
psTraceCrypto("Unable to parse ECC pubkey from cert\n");
return -1;
}
p += arcLen;
*pp = p;
return 0;
}
/**
Initialize an ECC key and generate a public/private keypair for the given
curve.
@param pool Memory pool
@param[out] key Uninitialized ECC key. This API will call psEccInitKey() on this key.
@param[in] curve ECC named curve to use for key.
@param[in] usrData User data pointer to pass to hardware implementations that use it.
@return < 0 on failure.
*/
int32_t psEccGenKey(psPool_t *pool, psEccKey_t *key, const psEccCurve_t *curve,
void *usrData)
{
int32_t err;
uint16_t keysize, slen;
psEccPoint_t *base;
pstm_int *A = NULL;
pstm_int prime, order, rand;
unsigned char *buf;
if (!key || !curve) {
psTraceCrypto("Only named curves supported in psEccGenKey\n");
return PS_UNSUPPORTED_FAIL;
}
psEccInitKey(pool, key, curve);
keysize = curve->size; /* Note, curve is non-null */
slen = keysize * 2;
/* allocate ram */
base = NULL;
buf = psMalloc(pool, ECC_MAXSIZE);
if (buf == NULL) {
psError("Memory allocation error in psEccGenKey\n");
err = PS_MEM_FAIL;
goto ERR_KEY;
}
/* Make sure random number is less than "order" */
if (pstm_init_for_read_unsigned_bin(pool, &order, keysize) < 0) {
err = PS_MEM_FAIL;
goto ERR_BUF;
}
if ((err = pstm_read_radix(pool, &order, key->curve->order, slen, 16))
!= PS_SUCCESS) {
pstm_clear(&order);
goto ERR_BUF;
}
/* make up random string */
RETRY_RAND:
if (matrixCryptoGetPrngData(buf, keysize, usrData) != keysize) {
err = PS_PLATFORM_FAIL;
pstm_clear(&order);
goto ERR_BUF;
}
if (pstm_init_for_read_unsigned_bin(pool, &rand, keysize) < 0) {
err = PS_MEM_FAIL;
pstm_clear(&order);
goto ERR_BUF;
}
if ((err = pstm_read_unsigned_bin(&rand, buf, keysize)) != PS_SUCCESS) {
pstm_clear(&order);
pstm_clear(&rand);
goto ERR_BUF;
}
/* Make sure random number is less than "order" */
if (pstm_cmp(&rand, &order) == PSTM_GT) {
pstm_clear(&rand);
goto RETRY_RAND;
}
pstm_clear(&rand);
pstm_clear(&order);
if (key->curve->isOptimized == 0) {
if ((A = psMalloc(pool, sizeof(pstm_int))) == NULL) {
err = PS_MEM_FAIL;
goto ERR_BUF;
}
if (pstm_init_for_read_unsigned_bin(pool, A, keysize) < 0) {
err = PS_MEM_FAIL;
psFree(A, pool);
goto ERR_BUF;
}
if ((err = pstm_read_radix(pool, A, key->curve->A, slen, 16))
!= PS_SUCCESS) {
goto ERR_A;
}
}
if (pstm_init_for_read_unsigned_bin(pool, &prime, keysize) < 0) {
err = PS_MEM_FAIL;
goto ERR_A;
}
base = eccNewPoint(pool, prime.alloc);
if (base == NULL) {
err = PS_MEM_FAIL;
goto ERR_PRIME;
}
/* read in the specs for this key */
if ((err = pstm_read_radix(pool, &prime, key->curve->prime, slen, 16))
!= PS_SUCCESS) {
goto ERR_BASE;
}
if ((err = pstm_read_radix(pool, &base->x, key->curve->Gx, slen, 16))
!= PS_SUCCESS){
goto ERR_BASE;
}
if ((err = pstm_read_radix(pool, &base->y, key->curve->Gy, slen, 16))
!= PS_SUCCESS){
goto ERR_BASE;
}
pstm_set(&base->z, 1);
if (pstm_init_for_read_unsigned_bin(pool, &key->k, keysize) < 0) {
err = PS_MEM_FAIL;
goto ERR_BASE;
}
if ((err = pstm_read_unsigned_bin(&key->k, buf, keysize))
!= PS_SUCCESS) {
goto ERR_BASE;
}
/* make the public key */
if (pstm_init_size(pool, &key->pubkey.x, (key->k.used * 2) + 1) < 0) {
err = PS_MEM_FAIL;
goto ERR_BASE;
}
if (pstm_init_size(pool, &key->pubkey.y, (key->k.used * 2) + 1) < 0) {
err = PS_MEM_FAIL;
goto ERR_BASE;
}
if (pstm_init_size(pool, &key->pubkey.z, (key->k.used * 2) + 1) < 0) {
err = PS_MEM_FAIL;
goto ERR_BASE;
}
if ((err = eccMulmod(pool, &key->k, base, &key->pubkey, &prime, 1, A)) !=
PS_SUCCESS) {
goto ERR_BASE;
}
key->type = PS_PRIVKEY;
/* frees for success */
eccFreePoint(base);
pstm_clear(&prime);
if (A) {
pstm_clear(A);
psFree(A, pool);
}
psFree(buf, pool);
return PS_SUCCESS;
ERR_BASE:
eccFreePoint(base);
ERR_PRIME:
pstm_clear(&prime);
ERR_A:
if (A) {
pstm_clear(A);
psFree(A, pool);
}
ERR_BUF:
psFree(buf, pool);
ERR_KEY:
psEccClearKey(key);
return err;
}
#if defined(MATRIX_USE_FILE_SYSTEM) && defined(USE_PRIVATE_KEY_PARSING)
/******************************************************************************/
/*
ECPrivateKey{CURVES:IOSet} ::= SEQUENCE {
version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
privateKey OCTET STRING,
parameters [0] Parameters{{IOSet}} OPTIONAL,
publicKey [1] BIT STRING OPTIONAL
}
*/
int32_t psEccParsePrivFile(psPool_t *pool, const char *fileName,
const char *password, psEccKey_t *key)
{
unsigned char *DERout;
int32_t rc;
uint16_t DERlen;
#ifdef USE_PKCS8
psPubKey_t pubkey;
#endif
if ((rc = pkcs1DecodePrivFile(pool, fileName, password, &DERout, &DERlen)) < 0) {
return rc;
}
if ((rc = psEccParsePrivKey(pool, DERout, DERlen, key, NULL)) < 0) {
#ifdef USE_PKCS8
/* This logic works for processing PKCS#8 files becuase the above file
and bin decodes will always leave the unprocessed buffer intact and
the password protection is done in the internal ASN.1 encoding */
if ((rc = pkcs8ParsePrivBin(pool, DERout, DERlen, (char*)password,
&pubkey)) < 0) {
psFree(DERout, pool);
return rc;
}
rc = psEccCopyKey(key, &pubkey.key.ecc);
psClearPubKey(&pubkey);
#else
psFree(DERout, pool);
return rc;
#endif
}
psFree(DERout, pool);
return PS_SUCCESS;
}
#endif /* MATRIX_USE_FILE_SYSTEM && USE_PRIVATE_KEY_PARSING */
int32_t psEccParsePrivKey(psPool_t *pool,
const unsigned char *keyBuf, uint16_t keyBufLen,
psEccKey_t *key, const psEccCurve_t *curve)
{
const psEccCurve_t *eccCurve;
const unsigned char *buf, *end;
uint8_t ignore_bits;
uint32_t oid;
int32_t asnInt;
uint16_t len;
buf = keyBuf;
end = buf + keyBufLen;
if (getAsnSequence(&buf, (uint16_t)(end - buf), &len) < 0) {
psTraceCrypto("ECDSA subject signature parse failure 1\n");
return PS_FAILURE;
}
if (getAsnInteger(&buf, (uint16_t)(end - buf), &asnInt) < 0 ||
asnInt != 1) {
psTraceCrypto("Expecting private key flag\n");
return PS_FAILURE;
}
/* Initial curve check */
if ((*buf++ != ASN_OCTET_STRING) ||
getAsnLength(&buf, (uint16_t)(end - buf), &len) < 0 ||
(uint16_t)(end - buf) < len ||
len < (MIN_ECC_BITS / 8)) {
psTraceCrypto("Expecting private key octet string\n");
return PS_FAILURE;
}
psEccInitKey(pool, key, curve);
if (pstm_init_for_read_unsigned_bin(pool, &key->k, len) != PS_SUCCESS) {
goto L_FAIL;
}
/* Key material */
if (pstm_read_unsigned_bin(&key->k, buf, len) != PS_SUCCESS) {
psTraceCrypto("Unable to read private key octet string\n");
goto L_FAIL;
}
key->type = PS_PRIVKEY;
buf += len;
if (*buf == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) {
/* optional parameters are present */
buf++;
if (getAsnLength(&buf, (uint16_t)(end - buf), &len) < 0 ||
(uint16_t)(end - buf) < len ||
len < 1) {
psTraceCrypto("Bad private key format\n");
goto L_FAIL;
}
if (*(buf++) != ASN_OID ||
getAsnLength(&buf, (uint16_t)(end - buf), &len) < 0 ||
(uint16_t)(end - buf) < len) {
psTraceCrypto("Only namedCurves are supported in EC keys\n");
goto L_FAIL;
}
/* Note len can be 0 here */
oid = 0;
while (len > 0) {
oid += *buf++;
len--;
}
if (getEccParamByOid(oid, &eccCurve) < 0) {
psTraceCrypto("Cert named curve not found in eccCurve list\n");
goto L_FAIL;
}
if (curve != NULL && curve != eccCurve) {
psTraceCrypto("PrivKey named curve doesn't match desired\n");
goto L_FAIL;
}
key->curve = eccCurve;
} else if (curve != NULL) {
key->curve = curve;
} else {
psTraceCrypto("No curve found in EC private key\n");
goto L_FAIL;
}
if (*buf == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1)) {
/* optional public key is present */
buf++;
if (getAsnLength(&buf, (uint16_t)(end - buf), &len) < 0 ||
(uint16_t)(end - buf) < len ||
len < 1) {
psTraceCrypto("Bad private key format\n");
goto L_FAIL;
}
if (*(buf++) != ASN_BIT_STRING ||
getAsnLength(&buf, (uint16_t)(end - buf), &len) < 0 ||
(uint16_t)(end - buf) < len ||
len < 1) {
goto L_FAIL;
}
ignore_bits = *buf++;
len--;
if (ignore_bits != 0) {
psTraceCrypto("Unexpected ECC pubkey format\n");
goto L_FAIL;
}
/* Note len can be 0 here */
if (psEccX963ImportKey(pool, buf, len, key, key->curve) < 0) {
psTraceCrypto("Unable to parse ECC pubkey from cert\n");
goto L_FAIL;
}
buf += len;
}
/* Should be at the end */
if (end != buf) {
/* If this stream came from an encrypted file, there could be
padding bytes on the end */
len = (uint16_t)(end - buf);
while (buf < end) {
if (*buf != len) {
psTraceCrypto("Problem at end of private key parse\n");
goto L_FAIL;
}
buf++;
}
}
return PS_SUCCESS;
L_FAIL:
psEccClearKey(key);
return PS_FAIL;
}
int32_t getEccParamById(uint16_t curveId, const psEccCurve_t **curve)
{
int i = 0;
/* A curveId of zero is asking for a default curver */
if (curveId == 0) {
*curve = &eccCurve[0];
return 0;
}
*curve = NULL;
while (eccCurve[i].size > 0) {
if (curveId == eccCurve[i].curveId) {
*curve = &eccCurve[i];
return 0;
}
i++;
}
return PS_FAIL;
}
int32_t getEccParamByOid(uint32_t oid, const psEccCurve_t **curve)
{
int i = 0;
*curve = NULL;
while (eccCurve[i].size > 0) {
if (oid == eccCurve[i].OIDsum) {
*curve = &eccCurve[i];
return 0;
}
i++;
}
return PS_FAIL;
}
int32_t getEccParamByName(const char *curveName,
const psEccCurve_t **curve)
{
int i = 0;
*curve = NULL;
while (eccCurve[i].size > 0) {
if (strcmp(curveName, eccCurve[i].name) == 0) {
*curve = &eccCurve[i];
return 0;
}
i++;
}
return PS_FAIL;
}
/**
Return a list of all supported curves.
This method will put the largest bit strength first in the list, because
of their order in the eccCurve[] array.
*/
void psGetEccCurveIdList(unsigned char *curveList, uint8_t *len)
{
uint16_t listLen = 0, i = 0;
while (eccCurve[i].size > 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (eccCurve[i].curveId & 0xFF00) >> 8;
curveList[listLen++] = eccCurve[i].curveId & 0xFF;
}
i++;
}
*len = listLen;
}
/**
User set list of curves they want to support.
This method will put the largest bit strength first in the list.
@param[in] curves Flags indicating which curves to use.
*/
void userSuppliedEccList(unsigned char *curveList, uint8_t *len, uint32_t curves)
{
const psEccCurve_t *curve;
uint8_t listLen = 0;
if (curves & IS_SECP521R1) {
if (getEccParamById(IANA_SECP521R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_BRAIN512R1) {
if (getEccParamById(IANA_BRAIN512R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_SECP384R1) {
if (getEccParamById(IANA_SECP384R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_BRAIN384R1) {
if (getEccParamById(IANA_BRAIN384R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_SECP256R1) {
if (getEccParamById(IANA_SECP256R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_BRAIN256R1) {
if (getEccParamById(IANA_BRAIN256R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_SECP224R1) {
if (getEccParamById(IANA_SECP224R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_BRAIN224R1) {
if (getEccParamById(IANA_BRAIN224R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
if (curves & IS_SECP192R1) {
if (getEccParamById(IANA_SECP192R1, &curve) == 0) {
if (listLen < (*len - 2)) {
curveList[listLen++] = (curve->curveId & 0xFF00) >> 8;
curveList[listLen++] = curve->curveId & 0xFF;
}
}
}
*len = listLen;
}
uint32_t compiledInEcFlags(void)
{
uint32_t ecFlags = 0;
#ifdef USE_SECP192R1
ecFlags |= IS_SECP192R1;
#endif
#ifdef USE_SECP224R1
ecFlags |= IS_SECP224R1;
#endif
#ifdef USE_SECP256R1
ecFlags |= IS_SECP256R1;
#endif
#ifdef USE_SECP384R1
ecFlags |= IS_SECP384R1;
#endif
#ifdef USE_SECP521R1
ecFlags |= IS_SECP521R1;
#endif
#ifdef USE_BRAIN224R1
ecFlags |= IS_BRAIN224R1;
#endif
#ifdef USE_BRAIN256R1
ecFlags |= IS_BRAIN256R1;
#endif
#ifdef USE_BRAIN384R1
ecFlags |= IS_BRAIN384R1;
#endif
#ifdef USE_BRAIN512R1
ecFlags |= IS_BRAIN512R1;
#endif
return ecFlags;
}
/******************************************************************************/
static uint8_t get_digit_count(const pstm_int *a)
{
return a->used;
}
static pstm_digit get_digit(const pstm_int *a, uint8_t n)
{
return (n >= a->used) ? (pstm_digit)0 : a->dp[n];
}
/******************************************************************************/
/**
Perform a point multiplication
@param[in] pool Memory pool
@param[in] k The scalar to multiply by
@param[in] G The base point
@param[out] R Destination for kG
@param modulus The modulus of the field the ECC curve is in
@param map Boolean whether to map back to affine or not (1==map)
@param[in,out] tmp_int Temporary scratch big integer (memory optimization)
@return PS_SUCCESS on success, < 0 on error
*/
/* size of sliding window, don't change this! */
#define ECC_MULMOD_WINSIZE 4
static int32_t eccMulmod(psPool_t *pool, const pstm_int *k, const psEccPoint_t *G,
psEccPoint_t *R, pstm_int *modulus, uint8_t map, pstm_int *tmp_int)
{
psEccPoint_t *tG, *M[8]; /* @note large on stack */
int32 i, j, err;
pstm_int mu;
pstm_digit mp;
unsigned long buf;
int32 first, bitbuf, bitcpy, bitcnt, mode, digidx;
/* init montgomery reduction */
if ((err = pstm_montgomery_setup(modulus, &mp)) != PS_SUCCESS) {
return err;
}
if ((err = pstm_init_size(pool, &mu, modulus->alloc)) != PS_SUCCESS) {
return err;
}
if ((err = pstm_montgomery_calc_normalization(&mu, modulus)) != PS_SUCCESS) {
pstm_clear(&mu);
return err;
}
/* alloc ram for window temps */
for (i = 0; i < 8; i++) {
M[i] = eccNewPoint(pool, (G->x.used * 2) + 1);
if (M[i] == NULL) {
for (j = 0; j < i; j++) {
eccFreePoint(M[j]);
}
pstm_clear(&mu);
return PS_MEM_FAIL;
}
}
/* make a copy of G incase R==G */
tG = eccNewPoint(pool, G->x.alloc);
if (tG == NULL) {
err = PS_MEM_FAIL;
goto done;
}
/* tG = G and convert to montgomery */
if (pstm_cmp_d(&mu, 1) == PSTM_EQ) {
if ((err = pstm_copy(&G->x, &tG->x)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&G->y, &tG->y)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&G->z, &tG->z)) != PS_SUCCESS) { goto done; }
} else {
if ((err = pstm_mulmod(pool, &G->x, &mu, modulus, &tG->x)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_mulmod(pool, &G->y, &mu, modulus, &tG->y)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_mulmod(pool, &G->z, &mu, modulus, &tG->z)) != PS_SUCCESS) {
goto done;
}
}
pstm_clear(&mu);
/* calc the M tab, which holds kG for k==8..15 */
/* M[0] == 8G */
if ((err = eccProjectiveDblPoint(pool, tG, M[0], modulus, &mp, tmp_int)) != PS_SUCCESS)
{
goto done;
}
if ((err = eccProjectiveDblPoint(pool, M[0], M[0], modulus, &mp, tmp_int)) !=
PS_SUCCESS) {
goto done;
}
if ((err = eccProjectiveDblPoint(pool, M[0], M[0], modulus, &mp, tmp_int)) !=
PS_SUCCESS) {
goto done;
}
/* now find (8+k)G for k=1..7 */
for (j = 9; j < 16; j++) {
if ((err = eccProjectiveAddPoint(pool, M[j-9], tG, M[j-8], modulus,
&mp, tmp_int)) != PS_SUCCESS) {
goto done;
}
}
/* setup sliding window */
mode = 0;
bitcnt = 1;
buf = 0;
digidx = get_digit_count(k) - 1;
bitcpy = bitbuf = 0;
first = 1;
/* perform ops */
for (;;) {
/* grab next digit as required */
if (--bitcnt == 0) {
if (digidx == -1) {
break;
}
buf = get_digit(k, digidx);
bitcnt = DIGIT_BIT;
--digidx;
}
/* grab the next msb from the ltiplicand */
i = (buf >> (DIGIT_BIT - 1)) & 1;
buf <<= 1;
/* skip leading zero bits */
if (mode == 0 && i == 0) {
continue;
}
/* if the bit is zero and mode == 1 then we double */
if (mode == 1 && i == 0) {
if ((err = eccProjectiveDblPoint(pool, R, R, modulus, &mp, tmp_int)) !=
PS_SUCCESS) {
goto done;
}
continue;
}
/* else we add it to the window */
bitbuf |= (i << (ECC_MULMOD_WINSIZE - ++bitcpy));
mode = 2;
if (bitcpy == ECC_MULMOD_WINSIZE) {
/* if this is the first window we do a simple copy */
if (first == 1) {
/* R = kG [k = first window] */
if ((err = pstm_copy(&M[bitbuf-8]->x, &R->x)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_copy(&M[bitbuf-8]->y, &R->y)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_copy(&M[bitbuf-8]->z, &R->z)) != PS_SUCCESS) {
goto done;
}
first = 0;
} else {
/* normal window */
/* ok window is filled so double as required and add */
/* double first */
for (j = 0; j < ECC_MULMOD_WINSIZE; j++) {
if ((err = eccProjectiveDblPoint(pool, R, R, modulus, &mp, tmp_int))
!= PS_SUCCESS) {
goto done;
}
}
/* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */
if ((err = eccProjectiveAddPoint(pool, R, M[bitbuf-8], R,
modulus, &mp, tmp_int)) != PS_SUCCESS) {
goto done;
}
}
/* empty window and reset */
bitcpy = bitbuf = 0;
mode = 1;
}
}
/* if bits remain then double/add */
if (mode == 2 && bitcpy > 0) {
/* double then add */
for (j = 0; j < bitcpy; j++) {
/* only double if we have had at least one add first */
if (first == 0) {
if ((err = eccProjectiveDblPoint(pool, R, R, modulus, &mp, tmp_int)) !=
PS_SUCCESS) {
goto done;
}
}
bitbuf <<= 1;
if ((bitbuf & (1 << ECC_MULMOD_WINSIZE)) != 0) {
if (first == 1){
/* first add, so copy */
if ((err = pstm_copy(&tG->x, &R->x)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_copy(&tG->y, &R->y)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_copy(&tG->z, &R->z)) != PS_SUCCESS) {
goto done;
}
first = 0;
} else {
/* then add */
if ((err = eccProjectiveAddPoint(pool, R, tG, R, modulus,
&mp, tmp_int)) != PS_SUCCESS) {
goto done;
}
}
}
}
}
/* map R back from projective space */
if (map) {
err = eccMap(pool, R, modulus, &mp);
} else {
err = PS_SUCCESS;
}
done:
pstm_clear(&mu);
eccFreePoint(tG);
for (i = 0; i < 8; i++) {
eccFreePoint(M[i]);
}
return err;
}
static int32 eccTestPoint(psPool_t *pool, psEccPoint_t *P, pstm_int *prime,
pstm_int *b)
{
pstm_int t1, t2;
uint32 paDlen;
pstm_digit *paD;
int32 err;
if ((err = pstm_init(pool, &t1)) < 0) {
return err;
}
if ((err = pstm_init(pool, &t2)) < 0) {
pstm_clear(&t1);
return err;
}
/* Pre-allocated digit. TODO: haven't fully explored max paDlen */
paDlen = (prime->used*2+1) * sizeof(pstm_digit);
if ((paD = psMalloc(pool, paDlen)) == NULL) {
pstm_clear(&t1);
pstm_clear(&t2);
return PS_MEM_FAIL;
}
/* compute y^2 */
if ((err = pstm_sqr_comba(pool, &P->y, &t1, paD, paDlen)) < 0) {
goto error;
}
/* compute x^3 */
if ((err = pstm_sqr_comba(pool, &P->x, &t2, paD, paDlen)) < 0) {
goto error;
}
if ((err = pstm_mod(pool, &t2, prime, &t2)) < 0) {
goto error;
}
if ((err = pstm_mul_comba(pool, &P->x, &t2, &t2, paD, paDlen)) < 0) {
goto error;
}
/* compute y^2 - x^3 */
if ((err = pstm_sub(&t1, &t2, &t1)) < 0) {
goto error;
}
/* compute y^2 - x^3 + 3x */
if ((err = pstm_add(&t1, &P->x, &t1)) < 0) {
goto error;
}
if ((err = pstm_add(&t1, &P->x, &t1)) < 0) {
goto error;
}
if ((err = pstm_add(&t1, &P->x, &t1)) < 0) {
goto error;
}
if ((err = pstm_mod(pool, &t1, prime, &t1)) < 0) {
goto error;
}
while (pstm_cmp_d(&t1, 0) == PSTM_LT) {
if ((err = pstm_add(&t1, prime, &t1)) < 0) {
goto error;
}
}
while (pstm_cmp(&t1, prime) != PSTM_LT) {
if ((err = pstm_sub(&t1, prime, &t1)) < 0) {
goto error;
}
}
/* compare to b */
if (pstm_cmp(&t1, b) != PSTM_EQ) {
psTraceCrypto("Supplied EC public point not on curve\n");
err = PS_LIMIT_FAIL;
} else {
err = PS_SUCCESS;
}
error:
psFree(paD, pool);
pstm_clear(&t1);
pstm_clear(&t2);
return err;
}
/******************************************************************************/
/**
ANSI X9.62 or X9.63 (Section 4.3.7) uncompressed import.
This function imports the public ECC key elements (the x, y and z values).
If a private 'k' value is defined, the public elements are added to the
key. Otherwise, only the public elements are loaded and the key
marked public.
The format of import is ASN.1, and is used both within certificate
parsing and when parsing public keys passed on the wire in TLS.
@param[in] pool Memory pool
@param[in] in ECC key data in uncompressed form
@param[in] inlen Length of destination and final output size
@param[in, out] key Key to import. Private keys types will not be
initialized, all others will.
@param[in] curve Curve parameters, may be NULL
@return PS_SUCCESS on success, < 0 on failure
*/
int32_t psEccX963ImportKey(psPool_t *pool,
const unsigned char *in, uint16_t inlen,
psEccKey_t *key, const psEccCurve_t *curve)
{
int32_t err;
pstm_int prime, b;
/* Must be odd and minimal size */
if (inlen < ((2 * (MIN_ECC_BITS / 8)) + 1) || (inlen & 1) == 0) {
return PS_ARG_FAIL;
}
/* The key passed in may be a private key that is already initialized
and the 'k' parameter set. */
if (key->type != PS_PRIVKEY) {
if (psEccInitKey(pool, key, curve) < 0) {
return PS_MEM_FAIL;
}
key->type = PS_PUBKEY;
}
if (pstm_init_for_read_unsigned_bin(pool, &key->pubkey.x,
(inlen - 1) >> 1) < 0) {
return PS_MEM_FAIL;
}
if (pstm_init_for_read_unsigned_bin(pool, &key->pubkey.y,
(inlen - 1) >> 1) < 0) {
pstm_clear(&key->pubkey.x);
return PS_MEM_FAIL;
}
if (pstm_init_size(pool, &key->pubkey.z, 1) < 0) {
pstm_clear(&key->pubkey.x);
pstm_clear(&key->pubkey.y);
return PS_MEM_FAIL;
}
switch (*in) {
/* Standard, supported format */
case ANSI_UNCOMPRESSED:
break;
/* Unsupported formats */
case ANSI_COMPRESSED0:
case ANSI_COMPRESSED1:
case ANSI_HYBRID0:
case ANSI_HYBRID1:
default:
psTraceCrypto("ERROR: ECC compressed/hybrid formats unsupported\n");
err = PS_UNSUPPORTED_FAIL;
goto error;
}
if ((err = pstm_read_unsigned_bin(&key->pubkey.x, (unsigned char *)in + 1,
(inlen - 1) >> 1)) != PS_SUCCESS) {
goto error;
}
if ((err = pstm_read_unsigned_bin(&key->pubkey.y,
(unsigned char *)in + 1 + ((inlen - 1) >> 1),
(inlen - 1) >> 1)) != PS_SUCCESS) {
goto error;
}
pstm_set(&key->pubkey.z, 1);
/* Validate the point is on the curve */
if (curve != NULL && curve->isOptimized) {
if ((err = pstm_init_for_read_unsigned_bin(pool, &prime, curve->size)) < 0) {
goto error;
}
if ((err = pstm_init_for_read_unsigned_bin(pool, &b, curve->size)) < 0) {
pstm_clear(&prime);
goto error;
}
if ((err = pstm_read_radix(pool, &prime, curve->prime,
curve->size * 2, 16)) < 0){
pstm_clear(&prime);
pstm_clear(&b);
goto error;
}
if ((err = pstm_read_radix(pool, &b, curve->B, curve->size * 2, 16)) < 0) {
pstm_clear(&prime);
pstm_clear(&b);
goto error;
}
if ((err = eccTestPoint(pool, &key->pubkey, &prime, &b)) < 0) {
pstm_clear(&prime);
pstm_clear(&b);
goto error;
}
pstm_clear(&prime);
pstm_clear(&b);
} else {
psTraceCrypto("WARNING: ECC public key not validated\n");
}
return PS_SUCCESS;
error:
psEccClearKey(key);
return err;
}
/******************************************************************************/
/**
ANSI X9.62 or X9.63 (Sec. 4.3.6) uncompressed export.
@param[in] pool Memory pool
@param[in] key Key to export
@param[out] out [out] destination of export
@param[in, out] outlen Length of destination and final output size
@return PS_SUCCESS on success, < 0 on failure
*/
int32_t psEccX963ExportKey(psPool_t *pool, const psEccKey_t *key,
unsigned char *out, uint16_t *outlen)
{
unsigned char buf[ECC_BUF_SIZE];
unsigned long numlen;
int32_t res;
numlen = key->curve->size;
if (*outlen < (1 + 2 * numlen)) {
*outlen = 1 + 2 * numlen;
return PS_LIMIT_FAIL;
}
out[0] = (unsigned char)ANSI_UNCOMPRESSED;
/* pad and store x */
memset(buf, 0, sizeof(buf));
if ((res = pstm_to_unsigned_bin(pool, &key->pubkey.x, buf +
(numlen - pstm_unsigned_bin_size(&key->pubkey.x)))) != PSTM_OKAY) {
return res;
}
memcpy(out+1, buf, numlen);
/* pad and store y */
memset(buf, 0, sizeof(buf));
if ((res = pstm_to_unsigned_bin(pool, &key->pubkey.y, buf +
(numlen - pstm_unsigned_bin_size(&key->pubkey.y)))) != PSTM_OKAY) {
return res;
}
memcpy(out+1+numlen, buf, numlen);
*outlen = 1 + 2*numlen;
return PS_SUCCESS;
}
/******************************************************************************/
/**
Create an ECC shared secret between two keys.
@param[in] pool Memory pool
@param[in] private_key The private ECC key
@param[in] public_key The public key
@param[out] out Destination of the shared secret (Conforms to EC-DH from ANSI X9.63)
@param[in,out] outlen The max size and resulting size of the shared secret
@param[in,out] usrData Opaque usrData for hardware offload.
@return PS_SUCCESS if successful
*/
int32_t psEccGenSharedSecret(psPool_t *pool,
const psEccKey_t *private_key, const psEccKey_t *public_key,
unsigned char *out, uint16_t *outlen,
void *usrData)
{
uint16_t x;
psEccPoint_t *result;
pstm_int *A = NULL;
pstm_int prime;
int32_t err;
/* type valid? */
if (private_key->type != PS_PRIVKEY) {
return PS_ARG_FAIL;
}
if (public_key->curve != NULL) {
if (private_key->curve != public_key->curve) {
return PS_ARG_FAIL;
}
}
/* make new point */
result = eccNewPoint(pool, (private_key->k.used * 2) + 1);
if (result == NULL) {
return PS_MEM_FAIL;
}
if (private_key->curve->isOptimized == 0)
{
if ((A = psMalloc(pool, sizeof(pstm_int))) == NULL) {
eccFreePoint(result);
return PS_MEM_FAIL;
}
if (pstm_init_for_read_unsigned_bin(pool, A, private_key->curve->size) < 0) {
psFree(A, pool);
eccFreePoint(result);
return PS_MEM_FAIL;
}
if ((err = pstm_read_radix(pool, A, private_key->curve->A,
private_key->curve->size * 2, 16))
!= PS_SUCCESS) {
pstm_clear(A);
psFree(A, pool);
eccFreePoint(result);
return err;
}
}
if ((err = pstm_init_for_read_unsigned_bin(pool, &prime,
private_key->curve->size)) != PS_SUCCESS) {
if (A) {
pstm_clear(A);
psFree(A, pool);
}
eccFreePoint(result);
return err;
}
if ((err = pstm_read_radix(pool, &prime, private_key->curve->prime,
private_key->curve->size * 2, 16)) != PS_SUCCESS){
goto done;
}
if ((err = eccMulmod(pool, &private_key->k, &public_key->pubkey, result,
&prime, 1, A)) != PS_SUCCESS) {
goto done;
}
x = pstm_unsigned_bin_size(&prime);
if (*outlen < x) {
*outlen = x;
err = PS_LIMIT_FAIL;
goto done;
}
memset(out, 0, x);
if ((err = pstm_to_unsigned_bin(pool, &result->x,
out + (x - pstm_unsigned_bin_size(&result->x)))) != PS_SUCCESS) {
goto done;
}
err = PS_SUCCESS;
*outlen = x;
done:
if (A) {
pstm_clear(A);
psFree(A, pool);
}
pstm_clear(&prime);
eccFreePoint(result);
return err;
}
/******************************************************************************/
/**
Add two ECC points
@param P The point to add
@param Q The point to add
@param[out] R The destination of the double
@param modulus The modulus of the field the ECC curve is in
@param mp The "b" value from montgomery_setup()
@return PS_SUCCESS on success
*/
static int32_t eccProjectiveAddPoint(psPool_t *pool, const psEccPoint_t *P,
const psEccPoint_t *Q, psEccPoint_t *R,
const pstm_int *modulus, const pstm_digit *mp, pstm_int *tmp_int)
{
pstm_int t1, t2, x, y, z;
pstm_digit *paD;
int32 err;
uint32 paDlen;
paD = NULL;
if (pstm_init_size(pool, &t1, P->x.alloc) < 0) {
return PS_MEM_FAIL;
}
err = PS_MEM_FAIL;
if (pstm_init_size(pool, &t2, P->x.alloc) < 0) {
goto ERR_T1;
}
if (pstm_init_size(pool, &x, P->x.alloc) < 0) {
goto ERR_T2;
}
if (pstm_init_size(pool, &y, P->y.alloc) < 0) {
goto ERR_X;
}
if (pstm_init_size(pool, &z, P->z.alloc) < 0) {
goto ERR_Y;
}
/* should we dbl instead? */
if ((err = pstm_sub(modulus, &Q->y, &t1)) != PS_SUCCESS) { goto done; }
if ((pstm_cmp(&P->x, &Q->x) == PSTM_EQ) &&
//(&Q->z != NULL && pstm_cmp(&P->z, &Q->z) == PSTM_EQ) &&
(pstm_cmp(&P->z, &Q->z) == PSTM_EQ) &&
(pstm_cmp(&P->y, &Q->y) == PSTM_EQ ||
pstm_cmp(&P->y, &t1) == PSTM_EQ)) {
pstm_clear_multi(&t1, &t2, &x, &y, &z, NULL, NULL, NULL);
return eccProjectiveDblPoint(pool, P, R, modulus, mp, tmp_int);
}
if ((err = pstm_copy(&P->x, &x)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&P->y, &y)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&P->z, &z)) != PS_SUCCESS) { goto done; }
/*
Pre-allocated digit. Used for mul, sqr, AND reduce
TODO: haven't fully explored max paDlen
*/
paDlen = (modulus->used * 2 + 1) * sizeof(pstm_digit);
if ((paD = psMalloc(pool, paDlen)) == NULL) {
err = PS_MEM_FAIL;
goto done;
}
/* if Z is one then these are no-operations */
if (pstm_cmp_d(&Q->z, 1) != PSTM_EQ) {
/* T1 = Z' * Z' */
if ((err = pstm_sqr_comba(pool, &Q->z, &t1, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* X = X * T1 */
if ((err = pstm_mul_comba(pool, &t1, &x, &x, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &x, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = Z' * T1 */
if ((err = pstm_mul_comba(pool, &Q->z, &t1, &t1, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* Y = Y * T1 */
if ((err = pstm_mul_comba(pool, &t1, &y, &y, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &y, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
}
/* T1 = Z*Z */
if ((err = pstm_sqr_comba(pool, &z, &t1, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T2 = X' * T1 */
if ((err = pstm_mul_comba(pool, &Q->x, &t1, &t2, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = Z * T1 */
if ((err = pstm_mul_comba(pool, &z, &t1, &t1, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = Y' * T1 */
if ((err = pstm_mul_comba(pool, &Q->y, &t1, &t1, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* Y = Y - T1 */
if ((err = pstm_sub(&y, &t1, &y)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&y, 0) == PSTM_LT) {
if ((err = pstm_add(&y, modulus, &y)) != PS_SUCCESS) { goto done; }
}
/* T1 = 2T1 */
if ((err = pstm_add(&t1, &t1, &t1)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t1, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
}
/* T1 = Y + T1 */
if ((err = pstm_add(&t1, &y, &t1)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t1, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
}
/* X = X - T2 */
if ((err = pstm_sub(&x, &t2, &x)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&x, 0) == PSTM_LT) {
if ((err = pstm_add(&x, modulus, &x)) != PS_SUCCESS) { goto done; }
}
/* T2 = 2T2 */
if ((err = pstm_add(&t2, &t2, &t2)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t2, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
}
/* T2 = X + T2 */
if ((err = pstm_add(&t2, &x, &t2)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t2, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
}
/* if Z' != 1 */
if (pstm_cmp_d(&Q->z, 1) != PSTM_EQ) {
/* Z = Z * Z' */
if ((err = pstm_mul_comba(pool, &z, &Q->z, &z, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &z, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
}
/* Z = Z * X */
if ((err = pstm_mul_comba(pool, &z, &x, &z, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &z, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = T1 * X */
if ((err = pstm_mul_comba(pool, &t1, &x, &t1, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* X = X * X */
if ((err = pstm_sqr_comba(pool, &x, &x, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &x, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T2 = T2 * x */
if ((err = pstm_mul_comba(pool, &t2, &x, &t2, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = T1 * X */
if ((err = pstm_mul_comba(pool, &t1, &x, &t1, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* X = Y*Y */
if ((err = pstm_sqr_comba(pool, &y, &x, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &x, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* X = X - T2 */
if ((err = pstm_sub(&x, &t2, &x)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&x, 0) == PSTM_LT) {
if ((err = pstm_add(&x, modulus, &x)) != PS_SUCCESS) { goto done; }
}
/* T2 = T2 - X */
if ((err = pstm_sub(&t2, &x, &t2)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&t2, 0) == PSTM_LT) {
if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
}
/* T2 = T2 - X */
if ((err = pstm_sub(&t2, &x, &t2)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&t2, 0) == PSTM_LT) {
if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
}
/* T2 = T2 * Y */
if ((err = pstm_mul_comba(pool, &t2, &y, &t2, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* Y = T2 - T1 */
if ((err = pstm_sub(&t2, &t1, &y)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&y, 0) == PSTM_LT) {
if ((err = pstm_add(&y, modulus, &y)) != PS_SUCCESS) { goto done; }
}
/* Y = Y/2 */
if (pstm_isodd(&y)) {
if ((err = pstm_add(&y, modulus, &y)) != PS_SUCCESS) { goto done; }
}
if ((err = pstm_div_2(&y, &y)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&x, &R->x)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&y, &R->y)) != PS_SUCCESS) { goto done; }
if ((err = pstm_copy(&z, &R->z)) != PS_SUCCESS) { goto done; }
err = PS_SUCCESS;
done:
pstm_clear(&z);
ERR_Y:
pstm_clear(&y);
ERR_X:
pstm_clear(&x);
ERR_T2:
pstm_clear(&t2);
ERR_T1:
pstm_clear(&t1);
if (paD) psFree(paD, pool);
return err;
}
/******************************************************************************/
/**
Double an ECC point
@param[in] P The point to double
@param[out] R The destination of the double
@param[in] modulus The modulus of the field the ECC curve is in
@param[in] mp The "b" value from montgomery_setup()
@param[in] A The "A" of the field the ECC curve is in
@return PS_SUCCESS on success
*/
static int32_t eccProjectiveDblPoint(psPool_t *pool, const psEccPoint_t *P,
psEccPoint_t *R, const pstm_int *modulus, const pstm_digit *mp,
const pstm_int *A)
{
pstm_int t1, t2;
pstm_digit *paD;
uint32 paDlen;
int32 err, initSize;
if (P != R) {
if (pstm_copy(&P->x, &R->x) < 0) { return PS_MEM_FAIL; }
if (pstm_copy(&P->y, &R->y) < 0) { return PS_MEM_FAIL; }
if (pstm_copy(&P->z, &R->z) < 0) { return PS_MEM_FAIL; }
}
initSize = R->x.used;
if (R->y.used > initSize) { initSize = R->y.used; }
if (R->z.used > initSize) { initSize = R->z.used; }
if (pstm_init_size(pool, &t1, (initSize * 2) + 1) < 0) {
return PS_MEM_FAIL;
}
if (pstm_init_size(pool, &t2, (initSize * 2) + 1) < 0) {
pstm_clear(&t1);
return PS_MEM_FAIL;
}
/*
Pre-allocated digit. Used for mul, sqr, AND reduce
TODO: haven't fully explored max possible paDlen
*/
paDlen = (modulus->used*2+1) * sizeof(pstm_digit);
if ((paD = psMalloc(pool, paDlen)) == NULL) {
err = PS_MEM_FAIL;
goto done;
}
/* t1 = Z * Z */
if ((err = pstm_sqr_comba(pool, &R->z, &t1, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t1, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* Z = Y * Z */
if ((err = pstm_mul_comba(pool, &R->z, &R->y, &R->z, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &R->z, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* Z = 2Z */
if ((err = pstm_add(&R->z, &R->z, &R->z)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&R->z, modulus) != PSTM_LT) {
if ((err = pstm_sub(&R->z, modulus, &R->z)) != PS_SUCCESS) {
goto done;
}
}
// compute into T1 M=3(X+Z^2)(X-Z^2)
if (A == NULL) {
/* T2 = X - T1 */
if ((err = pstm_sub(&R->x, &t1, &t2)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&t2, 0) == PSTM_LT) {
if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
}
/* T1 = X + T1 */
if ((err = pstm_add(&t1, &R->x, &t1)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t1, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
}
/* T2 = T1 * T2 */
if ((err = pstm_mul_comba(pool, &t1, &t2, &t2, paD, paDlen)) != PS_SUCCESS){
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = 2T2 */
if ((err = pstm_add(&t2, &t2, &t1)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t1, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
}
/* T1 = T1 + T2 */
if ((err = pstm_add(&t1, &t2, &t1)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t1, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
}
} else {
/* compute into T1 M=3X^2 + A Z^4 */
pstm_int t3, t4;
if (pstm_init_size(pool, &t3, (initSize * 2) + 1) < 0) {
return PS_MEM_FAIL;
}
if (pstm_init_size(pool, &t4, (initSize * 2) + 1) < 0) {
pstm_clear(&t3);
return PS_MEM_FAIL;
}
/* T3 = X * X */
if ((err = pstm_sqr_comba(pool, &R->x, &t3, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t3, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T4 = 2T3 */
if ((err = pstm_add(&t3, &t3, &t4)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t4, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t4, modulus, &t4)) != PS_SUCCESS) { goto done; }
}
/* T3 = T3 + T4 */
if ((err = pstm_add(&t3, &t4, &t3)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t3, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t3, modulus, &t3)) != PS_SUCCESS) { goto done; }
}
/* T4 = T1 * T1 */
if ((err = pstm_sqr_comba(pool, &t1, &t4, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_mod(pool, &t4, modulus, &t4)) != PS_SUCCESS) { goto done; }
/* T4 = T4 * A */
if ((err = pstm_mul_comba(pool, &t4, A, &t4, paD, paDlen)) != PS_SUCCESS){
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t4, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T1 = T3 + T4 */
if ((err = pstm_add(&t3, &t4, &t1)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&t1, modulus) != PSTM_LT) {
if ((err = pstm_sub(&t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
}
pstm_clear_multi(&t3, &t4, NULL, NULL, NULL, NULL, NULL, NULL);
}
/* Y = 2Y */
if ((err = pstm_add(&R->y, &R->y, &R->y)) != PS_SUCCESS) { goto done; }
if (pstm_cmp(&R->y, modulus) != PSTM_LT) {
if ((err = pstm_sub(&R->y, modulus, &R->y)) != PS_SUCCESS) { goto done;}
}
/* Y = Y * Y */
if ((err = pstm_sqr_comba(pool, &R->y, &R->y, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &R->y, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T2 = Y * Y */
if ((err = pstm_sqr_comba(pool, &R->y, &t2, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &t2, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* T2 = T2/2 */
if (pstm_isodd(&t2)) {
if ((err = pstm_add(&t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
}
if ((err = pstm_div_2(&t2, &t2)) != PS_SUCCESS) { goto done; }
/* Y = Y * X */
if ((err = pstm_mul_comba(pool, &R->y, &R->x, &R->y, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &R->y, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* X = T1 * T1 */
if ((err = pstm_sqr_comba(pool, &t1, &R->x, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &R->x, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* X = X - Y */
if ((err = pstm_sub(&R->x, &R->y, &R->x)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&R->x, 0) == PSTM_LT) {
if ((err = pstm_add(&R->x, modulus, &R->x)) != PS_SUCCESS) { goto done;}
}
/* X = X - Y */
if ((err = pstm_sub(&R->x, &R->y, &R->x)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&R->x, 0) == PSTM_LT) {
if ((err = pstm_add(&R->x, modulus, &R->x)) != PS_SUCCESS) { goto done;}
}
/* Y = Y - X */
if ((err = pstm_sub(&R->y, &R->x, &R->y)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&R->y, 0) == PSTM_LT) {
if ((err = pstm_add(&R->y, modulus, &R->y)) != PS_SUCCESS) { goto done;}
}
/* Y = Y * T1 */
if ((err = pstm_mul_comba(pool, &R->y, &t1, &R->y, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &R->y, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* Y = Y - T2 */
if ((err = pstm_sub(&R->y, &t2, &R->y)) != PS_SUCCESS) { goto done; }
if (pstm_cmp_d(&R->y, 0) == PSTM_LT) {
if ((err = pstm_add(&R->y, modulus, &R->y)) != PS_SUCCESS) { goto done;}
}
err = PS_SUCCESS;
done:
pstm_clear_multi(&t1, &t2, NULL, NULL, NULL, NULL, NULL, NULL);
if (paD) psFree(paD, pool);
return err;
}
/******************************************************************************/
/**
Allocate a new ECC point.
@return A newly allocated point or NULL on error
*/
static psEccPoint_t *eccNewPoint(psPool_t *pool, short size)
{
psEccPoint_t *p = NULL;
p = psMalloc(pool, sizeof(psEccPoint_t));
if (p == NULL) {
return NULL;
}
p->pool = pool;
if (size == 0) {
if (pstm_init(pool, &p->x) != PSTM_OKAY) {
goto ERR;
}
if (pstm_init(pool, &p->y) != PSTM_OKAY) {
goto ERR_X;
}
if (pstm_init(pool, &p->z) != PSTM_OKAY) {
goto ERR_Y;
}
} else {
if (pstm_init_size(pool, &p->x, size) != PSTM_OKAY) {
goto ERR;
}
if (pstm_init_size(pool, &p->y, size) != PSTM_OKAY) {
goto ERR_X;
}
if (pstm_init_size(pool, &p->z, size) != PSTM_OKAY) {
goto ERR_Y;
}
}
return p;
ERR_Y:
pstm_clear(&p->y);
ERR_X:
pstm_clear(&p->x);
ERR:
psFree(p, pool);
return NULL;
}
/**
Free an ECC point from memory.
@param p The point to free
*/
static void eccFreePoint(psEccPoint_t *p)
{
if (p != NULL) {
pstm_clear(&p->x);
pstm_clear(&p->y);
pstm_clear(&p->z);
psFree(p, p->pool);
}
}
/**
Map a projective jacbobian point back to affine space
@param[in,out] P [in/out] The point to map
@param[in] modulus The modulus of the field the ECC curve is in
@param[in] mp The "b" value from montgomery_setup()
@return PS_SUCCESS on success
*/
static int32_t eccMap(psPool_t *pool, psEccPoint_t *P, const pstm_int *modulus,
const pstm_digit *mp)
{
pstm_int t1, t2;
pstm_digit *paD;
int32 err;
uint32 paDlen;
if (pstm_init_size(pool, &t1, P->x.alloc) < 0) {
return PS_MEM_FAIL;
}
if (pstm_init_size(pool, &t2, P->x.alloc) < 0) {
pstm_clear(&t1);
return PS_MEM_FAIL;
}
/* Pre-allocated digit. Used for mul, sqr, AND reduce */
paDlen = (modulus->used*2+1) * sizeof(pstm_digit);
if ((paD = psMalloc(pool, paDlen)) == NULL) {
err = PS_MEM_FAIL;
goto done;
}
/* first map z back to normal */
if ((err = pstm_montgomery_reduce(pool, &P->z, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
/* get 1/z */
if ((err = pstm_invmod(pool, &P->z, modulus, &t1)) != PS_SUCCESS) {
goto done;
}
/* get 1/z^2 and 1/z^3 */
if ((err = pstm_sqr_comba(pool, &t1, &t2, paD, paDlen)) != PS_SUCCESS) {
goto done;
}
if ((err = pstm_mod(pool, &t2, modulus, &t2)) != PS_SUCCESS) { goto done; }
if ((err = pstm_mul_comba(pool, &t1, &t2, &t1, paD, paDlen)) != PS_SUCCESS){
goto done;
}
if ((err = pstm_mod(pool, &t1, modulus, &t1)) != PS_SUCCESS) { goto done; }
/* multiply against x/y */
if ((err = pstm_mul_comba(pool, &P->x, &t2, &P->x, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &P->x, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_mul_comba(pool, &P->y, &t1, &P->y, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
if ((err = pstm_montgomery_reduce(pool, &P->y, modulus, *mp, paD, paDlen))
!= PS_SUCCESS) {
goto done;
}
pstm_set(&P->z, 1);
err = PS_SUCCESS;
done:
pstm_clear_multi(&t1, &t2, NULL, NULL, NULL, NULL, NULL, NULL);
if (paD) psFree(paD, pool);
return err;
}
/******************************************************************************/
/**
Verify an ECDSA signature.
@param pool Memory pool
@param[in] key Public key to use for signature validation
@param[in] buf Data that is signed by private 'key'
@param[in] buflen Length in bytes of 'buf'
@param[in] sig Signature of 'buf' by the private key pair of 'key'
@param[in] siglen Length in bytes of 'sig'
@param[out] status Result of the signature check. 1 on success, -1 on
non-matching signature.
@param usrData Data used by some hardware crypto. Can be NULL.
@return < 0 on failure. Also 'status'.
*/
int32_t psEccDsaVerify(psPool_t *pool, const psEccKey_t *key,
const unsigned char *buf, uint16_t buflen,
const unsigned char *sig, uint16_t siglen,
int32_t *status, void *usrData)
{
psEccPoint_t *mG, *mQ;
pstm_digit mp;
pstm_int *A = NULL;
pstm_int v, w, u1, u2, e, p, m, r, s;
const unsigned char *c, *end;
int32_t err, radlen;
uint16_t len;
/* default to invalid signature */
*status = -1;
c = sig;
end = c + siglen;
if ((err = getAsnSequence(&c, (uint16_t)(end - c), &len)) < 0) {
psTraceCrypto("ECDSA subject signature parse failure 1\n");
return err;
}
if ((err = pstm_read_asn(pool, &c, (uint16_t)(end - c), &r)) < 0) {
psTraceCrypto("ECDSA subject signature parse failure 2\n");
return err;
}
if ((err = pstm_read_asn(pool, &c, (uint16_t)(end - c), &s)) < 0) {
psTraceCrypto("ECDSA subject signature parse failure 3\n");
pstm_clear(&r);
return err;
}
/* allocate ints */
radlen = key->curve->size * 2;
if (pstm_init_for_read_unsigned_bin(pool, &p, key->curve->size) < 0) {
pstm_clear(&s);
pstm_clear(&r);
return PS_MEM_FAIL;
}
err = PS_MEM_FAIL;
if (pstm_init_for_read_unsigned_bin(pool, &m, key->curve->size) < 0) {
goto LBL_P;
}
if (pstm_init_size(pool, &v, key->pubkey.x.alloc) < 0) {
goto LBL_M;
}
if (pstm_init_size(pool, &w, s.alloc) < 0) {
goto LBL_V;
}
/* Shouldn't have signed more data than the key length. Truncate if so */
if (buflen > key->curve->size) {
buflen = key->curve->size;
}
if (pstm_init_for_read_unsigned_bin(pool, &e, buflen) < 0) {
goto LBL_W;
}
if (pstm_init_size(pool, &u1, e.alloc + w.alloc) < 0) {
goto LBL_E;
}
if (pstm_init_size(pool, &u2, r.alloc + w.alloc) < 0) {
goto LBL_U1;
}
/* allocate points */
if ((mG = eccNewPoint(pool, key->pubkey.x.alloc * 2)) == NULL) {
goto LBL_U2;
}
if ((mQ = eccNewPoint(pool, key->pubkey.x.alloc * 2)) == NULL) {
goto LBL_MG;
}
/* get the order */
if ((err = pstm_read_radix(pool, &p, key->curve->order, radlen, 16))
!= PS_SUCCESS) {
goto error;
}
/* get the modulus */
if ((err = pstm_read_radix(pool, &m, key->curve->prime, radlen, 16))
!= PS_SUCCESS) {
goto error;
}
/* check for zero */
if (pstm_iszero(&r) || pstm_iszero(&s) || pstm_cmp(&r, &p) != PSTM_LT ||
pstm_cmp(&s, &p) != PSTM_LT) {
err = PS_PARSE_FAIL;
goto error;
}
/* read data */
if ((err = pstm_read_unsigned_bin(&e, buf, buflen)) != PS_SUCCESS) {
goto error;
}
/* w = s^-1 mod n */
if ((err = pstm_invmod(pool, &s, &p, &w)) != PS_SUCCESS) {
goto error;
}
/* u1 = ew */
if ((err = pstm_mulmod(pool, &e, &w, &p, &u1)) != PS_SUCCESS) {
goto error;
}
/* u2 = rw */
if ((err = pstm_mulmod(pool, &r, &w, &p, &u2)) != PS_SUCCESS) {
goto error;
}
/* find mG and mQ */
if ((err = pstm_read_radix(pool, &mG->x, key->curve->Gx, radlen, 16))
!= PS_SUCCESS) {
goto error;
}
if ((err = pstm_read_radix(pool, &mG->y, key->curve->Gy, radlen, 16))
!= PS_SUCCESS) {
goto error;
}
pstm_set(&mG->z, 1);
if ((err = pstm_copy(&key->pubkey.x, &mQ->x)) != PS_SUCCESS) {
goto error;
}
if ((err = pstm_copy(&key->pubkey.y, &mQ->y)) != PS_SUCCESS) {
goto error;
}
if ((err = pstm_copy(&key->pubkey.z, &mQ->z)) != PS_SUCCESS) {
goto error;
}
if (key->curve->isOptimized == 0)
{
if ((A = psMalloc(pool, sizeof(pstm_int))) == NULL) {
goto error;
}
if (pstm_init_for_read_unsigned_bin(pool, A, key->curve->size) < 0) {
goto error;
}
if ((err = pstm_read_radix(pool, A, key->curve->A,
key->curve->size * 2, 16))
!= PS_SUCCESS) {
goto error;
}
}
/* compute u1*mG + u2*mQ = mG */
if ((err = eccMulmod(pool, &u1, mG, mG, &m, 0, A)) != PS_SUCCESS) {
goto error;
}
if ((err = eccMulmod(pool, &u2, mQ, mQ, &m, 0, A)) != PS_SUCCESS) {
goto error;
}
/* find the montgomery mp */
if ((err = pstm_montgomery_setup(&m, &mp)) != PS_SUCCESS) {
goto error;
}
/* add them */
if ((err = eccProjectiveAddPoint(pool, mQ, mG, mG, &m, &mp, A)) != PS_SUCCESS) {
goto error;
}
/* reduce */
if ((err = eccMap(pool, mG, &m, &mp)) != PS_SUCCESS) {
goto error;
}
/* v = X_x1 mod n */
if ((err = pstm_mod(pool, &mG->x, &p, &v)) != PS_SUCCESS) {
goto error;
}
/* does v == r */
if (pstm_cmp(&v, &r) == PSTM_EQ) {
*status = 1;
}
/* clear up and return */
err = PS_SUCCESS;
error:
if (A) {
pstm_clear(A);
psFree(A, pool);
}
eccFreePoint(mQ);
LBL_MG:
eccFreePoint(mG);
LBL_U2:
pstm_clear(&u2);
LBL_U1:
pstm_clear(&u1);
LBL_E:
pstm_clear(&e);
LBL_W:
pstm_clear(&w);
LBL_V:
pstm_clear(&v);
LBL_M:
pstm_clear(&m);
LBL_P:
pstm_clear(&p);
pstm_clear(&s);
pstm_clear(&r);
return err;
}
/**
Sign a message digest.
@param pool Memory pool
@param[in] key Private ECC key
@param[in] in The data to sign
@param[in] inlen The length in bytes of 'in'
@param[out] out The destination for the signature
@param[in,out] outlen The max size and resulting size of the signature
@param[in] includeSize Pass 1 to include size prefix in output.
@param usrData Implementation specific data. Can pass NULL.
@return PS_SUCCESS if successful
@note TLS does use the size prefix in output.
*/
int32_t psEccDsaSign(psPool_t *pool, const psEccKey_t *privKey,
const unsigned char *buf, uint16_t buflen,
unsigned char *sig, uint16_t *siglen,
uint8_t includeSize, void *usrData)
{
psEccKey_t pubKey; /* @note Large on the stack */
pstm_int r, s;
pstm_int e, p;
uint16_t radlen;
int32_t err;
uint16_t olen, rLen, sLen;
uint32_t rflag, sflag, sanity;
unsigned char *negative;
rflag = sflag = 0;
err = 0;
/* is this a private key? */
if (privKey->type != PS_PRIVKEY) {
return PS_ARG_FAIL;
}
/* Can't sign more data than the key length. Truncate if so */
if (buflen > privKey->curve->size) {
buflen = privKey->curve->size;
}
err = PS_MEM_FAIL;
radlen = privKey->curve->size * 2;
if (pstm_init_for_read_unsigned_bin(pool, &p, privKey->curve->size) < 0) {
return PS_MEM_FAIL;
}
if (pstm_init_for_read_unsigned_bin(pool, &e, buflen) < 0) {
goto LBL_P;
}
if (pstm_init_size(pool, &r, p.alloc) < 0) {
goto LBL_E;
}
if (pstm_init_size(pool, &s, p.alloc) < 0) {
goto LBL_R;
}
if ((err = pstm_read_radix(pool, &p, privKey->curve->order, radlen,
16)) != PS_SUCCESS) {
goto errnokey;
}
if ((err = pstm_read_unsigned_bin(&e, buf, buflen)) != PS_SUCCESS) {
goto errnokey;
}
/* make up a key and export the public copy */
sanity = 0;
for (;;) {
if (sanity++ > 99) {
psTraceCrypto("ECC Signature sanity exceeded. Verify PRNG output.\n");
err = PS_PLATFORM_FAIL; /* possible problem with prng */
goto errnokey;
}
if ((err = psEccGenKey(pool, &pubKey, privKey->curve, usrData))
!= PS_SUCCESS) {
goto errnokey;
}
/* find r = x1 mod n */
if ((err = pstm_mod(pool, &pubKey.pubkey.x, &p, &r)) != PS_SUCCESS) {
goto error;
}
if (pstm_iszero(&r) == PS_TRUE) {
psEccClearKey(&pubKey);
} else {
/* find s = (e + xr)/k */
if ((err = pstm_invmod(pool, &pubKey.k, &p, &pubKey.k)) !=
PS_SUCCESS) {
goto error; /* k = 1/k */
}
if ((err = pstm_mulmod(pool, &privKey->k, &r, &p, &s))
!= PS_SUCCESS) {
goto error; /* s = xr */
}
if ((err = pstm_add(&e, &s, &s)) != PS_SUCCESS) {
goto error; /* s = e + xr */
}
if ((err = pstm_mod(pool, &s, &p, &s)) != PS_SUCCESS) {
goto error; /* s = e + xr */
}
if ((err = pstm_mulmod(pool, &s, &pubKey.k, &p, &s))
!= PS_SUCCESS) {
goto error; /* s = (e + xr)/k */
}
psEccClearKey(&pubKey);
rLen = pstm_unsigned_bin_size(&r);
sLen = pstm_unsigned_bin_size(&s);
/* Signatures can be smaller than the keysize but keep it sane */
if (((rLen + 2) >= privKey->curve->size) &&
((sLen + 2) >= privKey->curve->size)) {
if (pstm_iszero(&s) == PS_FALSE) {
break;
}
}
}
}
/* If r or s has the high bit set, the ASN.1 encoding should include
a leading 0x0 byte to prevent it from being "negative". */
negative = (unsigned char*)r.dp;
if (negative[rLen - 1] & 0x80) {
rLen++;
rflag = 1;
}
negative = (unsigned char*)s.dp;
if (negative[sLen - 1] & 0x80) { /* GOOD ONE */
sLen++;
sflag = 1;
}
olen = 6 + rLen + sLen;
/* Handle lengths longer than 128.. but still only handling up to 256 */
if (olen - 3 >= 128) {
olen++;
}
/* TLS uses a two byte length specifier. Others sometimes do not */
if (includeSize) {
if (olen + 2 > *siglen) {
err = -1;
goto errnokey;
}
*sig = olen >> 8 & 0xFF; sig++;
*sig = olen & 0xFF; sig++;
} else {
if (olen > *siglen) {
err = -1;
goto errnokey;
}
}
*sig = ASN_CONSTRUCTED | ASN_SEQUENCE; sig++;
if ((olen - 3) >= 128) {
*sig = 0x81; sig++; /* high bit to indicate 'long' and low for byte count */
*sig = (olen & 0xFF) - 3; sig++;
*siglen = 1;
} else {
*sig = (olen & 0xFF) - 2; sig++;
*siglen = 0;
}
*sig = ASN_INTEGER; sig++;
*sig = rLen & 0xFF; sig++;
if (includeSize) {
*siglen += 6;
} else {
*siglen += 4;
}
if (rflag) {
*sig = 0x0; sig++;
}
if ((err = pstm_to_unsigned_bin(pool, &r, sig)) != PSTM_OKAY) {
goto errnokey;
}
sig += rLen - rflag; /* Moved forward rflag already */
*siglen += rLen;
*sig = ASN_INTEGER; sig++;
*sig = sLen & 0xFF; sig++;
if (sflag) {
*sig = 0x0; sig++;
}
if ((err = pstm_to_unsigned_bin(pool, &s, sig)) != PSTM_OKAY) {
goto error;
}
sig += sLen - sflag; /* Moved forward sflag already */
*siglen += sLen + 2;
err = PS_SUCCESS;
goto errnokey;
error:
psEccClearKey(&pubKey);
errnokey:
pstm_clear(&s);
LBL_R:
pstm_clear(&r);
LBL_E:
pstm_clear(&e);
LBL_P:
pstm_clear(&p);
return err;
}
#endif /* USE_MATRIX_ECC */