Files
mars-matrixssl/crypto/pubkey/pubkey.c
2017-03-10 17:29:32 +02:00

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 */