575 lines
14 KiB
C
575 lines
14 KiB
C
/**
|
|
* @file pubkey.c
|
|
* @version $Format:%h%d$
|
|
*
|
|
* Public and Private key operations shared by crypto implementations.
|
|
*/
|
|
/*
|
|
* Copyright (c) 2013-2017 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 "../cryptoImpl.h"
|
|
|
|
#if defined(USE_RSA) || defined(USE_ECC)
|
|
|
|
/******************************************************************************/
|
|
|
|
int32_t psInitPubKey(psPool_t *pool, psPubKey_t *key, uint8_t type)
|
|
{
|
|
if (!key)
|
|
{
|
|
return PS_ARG_FAIL;
|
|
}
|
|
switch (type)
|
|
{
|
|
# ifdef USE_RSA
|
|
case PS_RSA:
|
|
psRsaInitKey(pool, &key->key.rsa);
|
|
break;
|
|
# endif
|
|
# ifdef USE_ECC
|
|
case PS_ECC:
|
|
psEccInitKey(pool, &key->key.ecc, NULL);
|
|
break;
|
|
# endif
|
|
default:
|
|
break;
|
|
}
|
|
key->pool = pool;
|
|
key->type = type;
|
|
key->keysize = 0;
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void psClearPubKey(psPubKey_t *key)
|
|
{
|
|
if (!key)
|
|
{
|
|
return;
|
|
}
|
|
switch (key->type)
|
|
{
|
|
# ifdef USE_RSA
|
|
case PS_RSA:
|
|
psRsaClearKey(&key->key.rsa);
|
|
break;
|
|
# endif
|
|
# ifdef USE_ECC
|
|
case PS_ECC:
|
|
psEccClearKey(&key->key.ecc);
|
|
break;
|
|
# endif
|
|
default:
|
|
break;
|
|
}
|
|
key->pool = NULL;
|
|
key->keysize = 0;
|
|
key->type = 0;
|
|
}
|
|
|
|
int32_t psNewPubKey(psPool_t *pool, uint8_t type, psPubKey_t **key)
|
|
{
|
|
int32_t rc;
|
|
|
|
if ((*key = psMalloc(pool, sizeof(psPubKey_t))) == NULL)
|
|
{
|
|
return PS_MEM_FAIL;
|
|
}
|
|
|
|
if ((rc = psInitPubKey(pool, *key, type)) < 0)
|
|
{
|
|
psFree(*key, pool);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void psDeletePubKey(psPubKey_t **key)
|
|
{
|
|
psClearPubKey(*key);
|
|
psFree(*key, NULL);
|
|
*key = NULL;
|
|
}
|
|
|
|
# ifdef USE_PRIVATE_KEY_PARSING
|
|
# ifdef MATRIX_USE_FILE_SYSTEM
|
|
# if defined(USE_ECC) && defined(USE_RSA)
|
|
/* Trial and error private key parse for when ECC or RSA is unknown.
|
|
|
|
pemOrDer should be 1 if PEM
|
|
|
|
Return codes:
|
|
1 RSA key
|
|
2 ECC key
|
|
-1 error
|
|
*/
|
|
int32_t psParseUnknownPrivKey(psPool_t *pool, int pemOrDer, char *keyfile,
|
|
char *password, psPubKey_t *privkey)
|
|
{
|
|
psRsaKey_t *rsakey;
|
|
psEccKey_t *ecckey;
|
|
int keytype = 1;
|
|
unsigned char *keyBuf;
|
|
int32 keyBufLen;
|
|
|
|
privkey->keysize = 0;
|
|
rsakey = &privkey->key.rsa;
|
|
ecckey = &privkey->key.ecc;
|
|
if (pemOrDer == 1)
|
|
{
|
|
/* PEM file. */
|
|
if (psPkcs1ParsePrivFile(pool, keyfile, password, rsakey)
|
|
< PS_SUCCESS)
|
|
{
|
|
/* psEccParsePrivFile will also try psPkcs8ParsePrivBin. */
|
|
if (psEccParsePrivFile(pool, keyfile, password, ecckey)
|
|
< PS_SUCCESS)
|
|
{
|
|
psTraceStrCrypto("Unable to parse private key file %s\n",
|
|
keyfile);
|
|
return -1;
|
|
}
|
|
keytype = 2;
|
|
}
|
|
else
|
|
{
|
|
keytype = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* DER file. */
|
|
if (psGetFileBuf(pool, keyfile, &keyBuf, &keyBufLen) < PS_SUCCESS)
|
|
{
|
|
psTraceStrCrypto("Unable to open private key file %s\n", keyfile);
|
|
return -1;
|
|
}
|
|
/* A raw RSAPrivateKey? */
|
|
if (psRsaParsePkcs1PrivKey(pool, keyBuf, keyBufLen, rsakey)
|
|
< PS_SUCCESS)
|
|
{
|
|
/* A raw ECPrivateKey? */
|
|
if (psEccParsePrivKey(pool, keyBuf, keyBufLen, ecckey, NULL)
|
|
< PS_SUCCESS)
|
|
{
|
|
# ifdef USE_PKCS8
|
|
/* A PKCS #8 PrivateKeyInfo containing an ECPrivateKey? */
|
|
if (psPkcs8ParsePrivBin(pool, keyBuf, keyBufLen, password,
|
|
privkey))
|
|
{
|
|
# endif /* USE_PKCS8 */
|
|
/* Nothing worked. */
|
|
psTraceCrypto("Unable to parse private key. " \
|
|
"Supported formats are RSAPrivateKey, " \
|
|
"ECPrivateKey and PKCS #8.\n");
|
|
psFree(keyBuf, pool);
|
|
return -1;
|
|
}
|
|
# ifdef USE_PKCS8
|
|
if (privkey->type == PS_RSA)
|
|
{
|
|
keytype = 1;
|
|
}
|
|
else if (privkey->type == PS_ECC)
|
|
{
|
|
keytype = 2;
|
|
}
|
|
goto parsed;
|
|
# endif /* USE_PKCS8 */
|
|
}
|
|
keytype = 2;
|
|
}
|
|
else
|
|
{
|
|
keytype = 1;
|
|
}
|
|
parsed:
|
|
psFree(keyBuf, pool);
|
|
}
|
|
|
|
if (keytype == 1)
|
|
{
|
|
privkey->type = PS_RSA;
|
|
privkey->keysize = psRsaSize(&privkey->key.rsa);
|
|
}
|
|
else
|
|
{
|
|
privkey->type = PS_ECC;
|
|
privkey->keysize = psEccSize(&privkey->key.ecc);
|
|
}
|
|
privkey->pool = pool;
|
|
return keytype;
|
|
}
|
|
|
|
/* Trial and error public key parse for when ECC or RSA is unknown.
|
|
|
|
pemOrDer should be 1 if PEM
|
|
|
|
Note: The current version of this function only supports RSA when
|
|
MatrixSSL's stock cryptographic library is used and
|
|
additionally ECC when CL cryptographic library is used.
|
|
|
|
Return codes:
|
|
1 RSA key
|
|
2 ECC key
|
|
-1 error
|
|
*/
|
|
int32_t psParseUnknownPubKey(psPool_t *pool, int pemOrDer, char *keyfile,
|
|
const char *password, psPubKey_t *pubkey)
|
|
{
|
|
psRsaKey_t *rsakey;
|
|
int keytype = 1;
|
|
unsigned char *keyBuf;
|
|
int32 keyBufLen;
|
|
|
|
/* flps_parseUnknownPubKey() is similar function.
|
|
First try to invoke that. */
|
|
|
|
(void) password; /* password is for future extensions. */
|
|
pubkey->keysize = 0;
|
|
rsakey = &pubkey->key.rsa;
|
|
if (pemOrDer == 1)
|
|
{
|
|
/* PEM file. */
|
|
if (psPkcs1ParsePubFile(pool, keyfile, rsakey)
|
|
< PS_SUCCESS)
|
|
{
|
|
return PS_FAILURE;
|
|
}
|
|
|
|
pubkey->type = PS_RSA;
|
|
pubkey->keysize = psRsaSize(&pubkey->key.rsa);
|
|
return keytype;
|
|
}
|
|
else
|
|
{
|
|
/* DER file. */
|
|
if (psGetFileBuf(pool, keyfile, &keyBuf, &keyBufLen) < PS_SUCCESS)
|
|
{
|
|
psTraceStrCrypto("Unable to open public key file %s\n", keyfile);
|
|
return -1;
|
|
}
|
|
/* Processing DER files not handled by current implementation of
|
|
the function the input shall be in PEM format. */
|
|
psFree(keyBuf, pool);
|
|
return PS_FAILURE;
|
|
}
|
|
}
|
|
|
|
# endif /* USE_ECC && USE_RSA */
|
|
# endif /* MATRIX_USE_FILE_SYSTEM */
|
|
# endif /* USE_PRIVATE_KEY_PARSING */
|
|
|
|
int32_t psHashLenToSigAlg(psSize_t hash_len,
|
|
uint8_t key_type)
|
|
{
|
|
int32_t signatureAlgorithm;
|
|
|
|
/**/
|
|
psAssert(key_type == PS_RSA || key_type == PS_ECC);
|
|
|
|
switch (hash_len)
|
|
{
|
|
# if defined(USE_MD2) || defined(USE_MD5)
|
|
case MD2_HASH_SIZE:
|
|
if (key_type == PS_RSA)
|
|
{
|
|
psTraceCrypto("pubRsaDecryptSignedElement cannot handle ");
|
|
psTraceCrypto("RSA-MD2 or RSA-MD5 signatures; please use ");
|
|
psTraceCrypto("pubRsaDecryptSignedElementExt instead.\n");
|
|
return PS_ARG_FAIL;
|
|
}
|
|
else
|
|
{
|
|
psTraceCrypto("ECDSA-MD2 and ECDSA-MD5 not supported\n");
|
|
return PS_UNSUPPORTED_FAIL;
|
|
}
|
|
break;
|
|
# endif /* USE_MD2 || USE_MD5 */
|
|
case SHA1_HASH_SIZE:
|
|
if (key_type == PS_RSA)
|
|
{
|
|
signatureAlgorithm = OID_SHA1_RSA_SIG;
|
|
}
|
|
else
|
|
{
|
|
signatureAlgorithm = OID_SHA1_ECDSA_SIG;
|
|
}
|
|
break;
|
|
# if 0
|
|
case SHA224_HASH_SIZE:
|
|
if (key_type == PS_RSA)
|
|
{
|
|
signatureAlgorithm = OID_SHA224_RSA_SIG;
|
|
}
|
|
else
|
|
{
|
|
signatureAlgorithm = OID_SHA224_ECDSA_SIG;
|
|
}
|
|
break;
|
|
# endif
|
|
case SHA256_HASH_SIZE:
|
|
if (key_type == PS_RSA)
|
|
{
|
|
signatureAlgorithm = OID_SHA256_RSA_SIG;
|
|
}
|
|
else
|
|
{
|
|
signatureAlgorithm = OID_SHA256_ECDSA_SIG;
|
|
}
|
|
break;
|
|
case SHA384_HASH_SIZE:
|
|
if (key_type == PS_RSA)
|
|
{
|
|
signatureAlgorithm = OID_SHA384_RSA_SIG;
|
|
}
|
|
else
|
|
{
|
|
signatureAlgorithm = OID_SHA384_ECDSA_SIG;
|
|
}
|
|
break;
|
|
case SHA512_HASH_SIZE:
|
|
if (key_type == PS_RSA)
|
|
{
|
|
signatureAlgorithm = OID_SHA512_RSA_SIG;
|
|
}
|
|
else
|
|
{
|
|
signatureAlgorithm = OID_SHA512_ECDSA_SIG;
|
|
}
|
|
break;
|
|
default:
|
|
psTraceCrypto("Unsupported hash size in RSA signature\n");
|
|
return PS_UNSUPPORTED_FAIL;
|
|
}
|
|
|
|
return signatureAlgorithm;
|
|
}
|
|
|
|
|
|
psRes_t psComputeHashForSig(const unsigned char *dataBegin,
|
|
psSizeL_t dataLen,
|
|
int32_t signatureAlgorithm,
|
|
unsigned char hashOut[SHA512_HASH_SIZE],
|
|
psSize_t *hashOutLen)
|
|
{
|
|
psDigestContext_t hash;
|
|
|
|
if (hashOut == NULL || hashOutLen == NULL)
|
|
{
|
|
return PS_ARG_FAIL;
|
|
}
|
|
|
|
if (dataLen < 1)
|
|
{
|
|
return PS_ARG_FAIL;
|
|
}
|
|
|
|
switch (signatureAlgorithm)
|
|
{
|
|
# ifdef ENABLE_MD5_SIGNED_CERTS
|
|
# ifdef USE_MD2
|
|
case OID_MD2_RSA_SIG:
|
|
psMd2Init(&hash.md2);
|
|
if (psMd2Update(&hash.md2, dataBegin, dataLen) < 0)
|
|
{
|
|
return PS_FAILURE;
|
|
}
|
|
if (psMd2Final(&hash.md2, hashOut) < 0)
|
|
{
|
|
return PS_FAILURE;
|
|
}
|
|
*hashOutLen = MD5_HASH_SIZE;
|
|
break;
|
|
# endif /* USE_MD2 */
|
|
case OID_MD5_RSA_SIG:
|
|
if (psMd5Init(&hash.md5) < 0)
|
|
{
|
|
return PS_FAILURE;
|
|
}
|
|
psMd5Update(&hash.md5, dataBegin, dataLen);
|
|
psMd5Final(&hash.md5, hashOut);
|
|
*hashOutLen = MD5_HASH_SIZE;
|
|
break;
|
|
# endif /* ENABLE_MD5_SIGNED_CERTS */
|
|
case OID_SHA1_RSA_SIG:
|
|
case OID_SHA1_RSA_SIG2:
|
|
case OID_SHA1_ECDSA_SIG:
|
|
psSha1PreInit(&hash.sha1);
|
|
psSha1Init(&hash.sha1);
|
|
psSha1Update(&hash.sha1, dataBegin, dataLen);
|
|
psSha1Final(&hash.sha1, hashOut);
|
|
*hashOutLen = SHA1_HASH_SIZE;
|
|
break;
|
|
case OID_SHA256_RSA_SIG:
|
|
case OID_SHA256_ECDSA_SIG:
|
|
psSha256PreInit(&hash.sha256);
|
|
psSha256Init(&hash.sha256);
|
|
psSha256Update(&hash.sha256, dataBegin, dataLen);
|
|
psSha256Final(&hash.sha256, hashOut);
|
|
*hashOutLen = SHA256_HASH_SIZE;
|
|
break;
|
|
# ifdef USE_SHA384
|
|
case OID_SHA384_RSA_SIG:
|
|
case OID_SHA384_ECDSA_SIG:
|
|
psSha512PreInit(&hash.sha512);
|
|
psSha384Init(&hash.sha384);
|
|
psSha384Update(&hash.sha384, dataBegin, dataLen);
|
|
psSha384Final(&hash.sha384, hashOut);
|
|
*hashOutLen = SHA384_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
# ifdef USE_SHA512
|
|
case OID_SHA512_RSA_SIG:
|
|
case OID_SHA512_ECDSA_SIG:
|
|
psSha512PreInit(&hash.sha512);
|
|
psSha512Init(&hash.sha512);
|
|
psSha512Update(&hash.sha512, dataBegin, dataLen);
|
|
psSha512Final(&hash.sha512, hashOut);
|
|
*hashOutLen = SHA512_HASH_SIZE;
|
|
break;
|
|
# endif
|
|
default:
|
|
psTraceCrypto("Unsupported sig alg\n");
|
|
return PS_UNSUPPORTED_FAIL;
|
|
}
|
|
|
|
return PS_SUCCESS;
|
|
}
|
|
|
|
psRes_t psVerifySig(psPool_t *pool,
|
|
const unsigned char hashIn[SHA512_HASH_SIZE],
|
|
psSize_t hashInLen,
|
|
const unsigned char *sig,
|
|
psSize_t sigLen,
|
|
psPubKey_t *key,
|
|
int32_t signatureAlgorithm,
|
|
psBool_t *verifyResult,
|
|
psVerifySigOptions_t *opts)
|
|
{
|
|
unsigned char out[SHA512_HASH_SIZE] = { 0 };
|
|
# ifdef USE_ECC
|
|
int32 eccRet;
|
|
# endif
|
|
|
|
psRes_t rc = PS_SUCCESS;
|
|
|
|
if (pool == NULL)
|
|
{
|
|
}
|
|
|
|
*verifyResult = PS_FALSE;
|
|
|
|
switch (key->type)
|
|
{
|
|
# ifdef USE_RSA
|
|
case PS_RSA:
|
|
if (pubRsaDecryptSignedElementExt(pool, &key->key.rsa,
|
|
(unsigned char *) sig, sigLen, out,
|
|
hashInLen, signatureAlgorithm, NULL) < 0)
|
|
{
|
|
psTraceCrypto("Error decrypting request signature\n");
|
|
rc = PS_FAILURE;
|
|
goto out;
|
|
}
|
|
if (memcmpct(hashIn, out, hashInLen) != 0)
|
|
{
|
|
rc = PS_VERIFICATION_FAILED;
|
|
*verifyResult = PS_FALSE;
|
|
goto out;
|
|
}
|
|
break;
|
|
# endif /* USE_RSA */
|
|
# ifdef USE_ECC
|
|
case PS_ECC:
|
|
if (psEccDsaVerify(pool, &key->key.ecc, hashIn,
|
|
hashInLen, sig, sigLen, &eccRet, NULL) < 0)
|
|
{
|
|
psTraceCrypto("Error decrypting request signature\n");
|
|
rc = PS_FAILURE;
|
|
goto out;
|
|
}
|
|
if (eccRet != 1)
|
|
{
|
|
psTraceCrypto("Error validating signature\n");
|
|
rc = PS_VERIFICATION_FAILED;
|
|
*verifyResult = PS_FALSE;
|
|
goto out;
|
|
}
|
|
break;
|
|
# endif /* USE_ECC */
|
|
default:
|
|
psTraceCrypto("Unsupported pubkey algorithm\n");
|
|
rc = PS_UNSUPPORTED_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
*verifyResult = PS_TRUE;
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
psRes_t psHashDataAndVerifySig(psPool_t *pool,
|
|
const unsigned char *dataBegin,
|
|
const psSizeL_t dataLen,
|
|
const unsigned char *sig,
|
|
psSize_t sigLen,
|
|
psPubKey_t *key,
|
|
int32_t signatureAlgorithm,
|
|
psBool_t *verifyResult,
|
|
psVerifySigOptions_t *opts)
|
|
{
|
|
unsigned char digest[SHA512_HASH_SIZE] = { 0 };
|
|
psSize_t digestLen = 0;
|
|
psRes_t rc;
|
|
|
|
*verifyResult = PS_FALSE;
|
|
|
|
rc = psComputeHashForSig(dataBegin, dataLen,
|
|
signatureAlgorithm, digest,
|
|
&digestLen);
|
|
if (rc != PS_SUCCESS)
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
rc = psVerifySig(pool,
|
|
digest, digestLen,
|
|
sig, sigLen,
|
|
key, signatureAlgorithm,
|
|
verifyResult,
|
|
opts);
|
|
return rc;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
#endif /* USE_RSA || USE_ECC */
|
|
|