Files
mars-matrixssl/matrixssl/matrixsslKeys.c
Janne Johansson 69b5f2c6c3 MatrixSSL 4.5.1
2022-07-29 12:30:12 +03:00

1669 lines
42 KiB
C

/**
* @file matrixsslKeys.c
* @version $Format:%h%d$
*
* The session and authentication management portions of the MatrixSSL library.
*/
/*
* Copyright (c) 2013-2018 Rambus Inc.
* 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 Rambus at
* http://www.rambus.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
*/
/******************************************************************************/
#ifndef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE 200112L
#endif
#ifndef _DEFAULT_SOURCE
# define _DEFAULT_SOURCE
#endif
#include "osdep_stdio.h"
#include "matrixsslImpl.h"
#if defined(USE_CA_CERTIFICATES) || defined(USE_IDENTITY_CERTIFICATES)
# ifdef USE_ROT_CRYPTO
# include "../crypto-rot/rotCommon.h"
# endif
# ifdef USE_CERT_PARSE
static
psRes_t handleAuthFailDate(matrixSslLoadKeysOpts_t *opts)
{
# ifdef POSIX /* TODO - implement date check on WIN32, etc. */
if (opts && (opts->flags & LOAD_KEYS_OPT_ALLOW_OUT_OF_DATE_CERT_PARSE))
{
/* Caller deems this as OK. */
psTraceInfo("Warning: loaded an out-of-date cert\n");
return PS_SUCCESS;
}
psTraceErrr("Error: Tried to load an out-of-date cert\n");
return PS_CERT_AUTH_FAIL_EXTENSION;
# endif /* POSIX */
return PS_SUCCESS;
}
# endif
static
psRes_t checkAuthFailFlags(psX509Cert_t *leafCert,
matrixSslLoadKeysOpts_t *opts)
{
# ifndef USE_CERT_PARSE
return PS_SUCCESS;
# else
if (leafCert->authFailFlags == 0)
{
return PS_SUCCESS; /* All OK. */
}
switch (leafCert->authFailFlags)
{
case PS_CERT_AUTH_FAIL_DATE_FLAG:
return handleAuthFailDate(opts);
default:
psAssert(PS_FALSE);
psTraceIntInfo("checkAuthFailFlags: add handling for flag: %u\n",
leafCert->authFailFlags);
return PS_FAILURE;
break;
}
return PS_SUCCESS;
# endif /* USE_CERT_PARSE */
}
/* On client add server cert trust anchors, and on server, add trusted client
certificate issuers. Inputs: either capath or cacert + cacert_len. */
static psRes_t
matrixSslAddTrustAnchors(sslKeys_t *keys,
const char *capath,
const unsigned char *cacert,
psSizeL_t cacert_len,
matrixSslLoadKeysOpts_t *opts)
{
psRes_t err = PS_SUCCESS;
/* Not necessary to store binary representations of CA certs */
int32 flags = 0;
flags |= CERT_STORE_DN_BUFFER;
flags |= CERT_ALLOW_BUNDLE_PARTIAL_PARSE;
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
# ifdef MATRIX_USE_FILE_SYSTEM
if (capath)
{
err = psX509ParseCertFile(keys->pool, capath, &keys->CAcerts, flags);
}
else
# endif /* MATRIX_USE_FILE_SYSTEM */
{
if (cacert == NULL || cacert_len == 0)
{
return PS_SUCCESS;
}
err = psX509ParseCertData(keys->pool,
cacert,
cacert_len,
&keys->CAcerts,
flags);
}
if (err == 0)
{
psTraceErrr("Failed to load any CA certs.\n");
return PS_PARSE_FAIL;
}
else if (err > 0)
{
psTraceIntInfo("Loaded %d CA certs\n", err);
err = checkAuthFailFlags(keys->CAcerts, opts);
if (err != PS_SUCCESS)
{
psX509FreeCert(keys->CAcerts);
keys->CAcerts = NULL;
return err;
}
return PS_SUCCESS;
}
else
{
/* Generic load failure path. Other paths have returned. */
}
psTraceStrInfo("Failed to load CA cert file: %s\n", (char *)capath);
# endif
return err;
}
#else /* CA or IDENTITY CERTIFICATES */
# define matrixSslAddTrustAnchors(k,capath,cacert,cacert_len,opts) (0)
# define checkAuthFailFlags(leafCert,opts) (0)
#endif /* CA or IDENTITY CERTIFICATES */
/* Have seen cases where the PKCS#12 files are not in a child-to-parent order.
This function is also used in TLS 1.3, where the spec allows chains to be
sent in arbitrary order. */
void matrixSslReorderCertChain(psX509Cert_t *a_cert)
{
#ifdef USE_CERT_PARSE
psX509Cert_t *prevCert = NULL;
psX509Cert_t *nextCert = NULL;
psX509Cert_t *currCert = a_cert;
while (currCert)
{
nextCert = currCert->next;
while (nextCert && Memcmp(currCert->issuer.hash, nextCert->subject.hash,
SHA1_HASH_SIZE) != 0)
{
prevCert = nextCert;
nextCert = nextCert->next;
if (nextCert && Memcmp(currCert->issuer.hash,
nextCert->subject.hash, SHA1_HASH_SIZE) == 0)
{
prevCert->next = nextCert->next;
nextCert->next = currCert->next;
currCert->next = nextCert;
break;
}
}
currCert = currCert->next;
}
#else
return;
#endif
}
#if defined(USE_IDENTITY_CERTIFICATES)
/******************************************************************************/
/*
Validate the cert chain and the private key for the material passed
to matrixSslReadKeys. Good to catch any user certifiate errors as
soon as possible
When the client private key is stored externally, skip all tests
involving the private key, since MatrixSSL does not have direct
access to the key.
*/
static psRes_t verifyReadKeyPair(psPool_t *pool, sslIdentity_t *p, void *poolUserPtr)
{
# ifndef USE_EXT_CERTIFICATE_VERIFY_SIGNING
/* Not allowed to have a certificate with no matching private key or
private key with no cert to match with */
if (p->cert != NULL && p->privKey.type == 0)
{
psTraceErrr("No private key given to matrixSslReadKeys cert\n");
return PS_CERT_AUTH_FAIL;
}
if (p->privKey.type != 0 && p->cert == NULL)
{
psTraceErrr("No cert given with private key to matrixSslReadKeys\n");
return PS_CERT_AUTH_FAIL;
}
# endif /* USE_EXT_CERTIFICATE_VERIFY_SIGNING */
/*
If this is a chain, we can validate it here with psX509AuthenticateCert
Don't check the error return code from this call because the chaining
usage restrictions will test parent-most cert for self-signed.
But we can look at 'authStatus' on all but the final cert to see if the
rest looks good
*/
if (p->cert != NULL && p->cert->next != NULL)
{
# ifdef USE_CERT_PARSE
psX509Cert_t *tmp, *found = NULL;
(void)psX509AuthenticateCert(pool, p->cert, NULL, &found, NULL, poolUserPtr);
for (tmp = p->cert; tmp && tmp->next; tmp = tmp->next)
{
if (tmp->authStatus != PS_TRUE)
{
psTraceErrr("Failed to authenticate cert chain\n");
return PS_CERT_AUTH_FAIL;
}
}
# endif
# ifndef USE_EXT_CERTIFICATE_VERIFY_SIGNING
# if defined(USE_RSA) && defined(USE_CERT_PARSE)
if (p->privKey.type == PS_RSA)
{
if (psRsaCmpPubKey(&p->privKey.key.rsa,
&p->cert->publicKey.key.rsa) < 0)
{
psTraceErrr("Private key doesn't match cert\n");
return PS_CERT_AUTH_FAIL;
}
}
# endif /* USE_RSA */
# endif /* !USE_EXT_CERTIFICATE_VERIFY_SIGNING */
}
return PS_SUCCESS;
}
/* Create Identity */
sslIdentity_t *
matrixSslMakeIdentity(psPool_t *pool, psPubKey_t idkey, psX509Cert_t *cert)
{
sslIdentity_t *identity;
if ((identity = psCalloc(pool, 1, sizeof(*identity))) != NULL)
{
identity->privKey = idkey;
identity->cert = cert;
identity->next = NULL;
}
return identity;
}
/* Free Identity */
void
matrixSslFreeIdentity(psPool_t *pool, sslIdentity_t *identity)
{
if (identity)
{
psX509FreeCert(identity->cert);
psClearPubKey(&identity->privKey);
psFree(identity, pool);
}
}
/* Clear Identity Assignment from SSL keys */
void
matrixSslDelIdentity(sslKeys_t *keys, sslIdentity_t *identity)
{
sslIdentity_t **p;
for (p = &(keys->identity); *p && *p != identity; p = &((*p)->next))
;
if (*p)
{
psAssert(*p == identity);
*p = (*p)->next;
}
}
/* Add Identity Assignment to SSL keys - they keys will take ownership of the
identity, and thus assigned identity needs to be removed from keys before
being deallocated. Function returns the 'identity' argument, and can't
fail. */
sslIdentity_t *
matrixSslAddIdentity(sslKeys_t *keys, sslIdentity_t *identity)
{
# if defined(USE_SERVER_SIDE_SSL) || defined(USE_CLIENT_AUTH)
sslIdentity_t **p;
/* Push the new key at the end of the list, so that routines using the
keys can easily prefer the first one added. */
for (p = &(keys->identity); *p; p = &((*p)->next))
;
*p = identity;
psTraceInfo("Adding identity with key type: ");
psTracePrintPubKeyTypeAndSize(NULL, &identity->privKey);
return identity;
# else
return NULL;
# endif
}
/* Create Identity from the keypair, and corresponding certificate chain. This
function will verify that certificate chain is properly ordered, and that
the subject certificate matches the subject key. */
sslIdentity_t *
matrixSslCreateIdentity(sslKeys_t *keys, psPubKey_t idkey, psX509Cert_t *cert)
{
sslIdentity_t *identity;
matrixSslReorderCertChain(cert);
identity = matrixSslMakeIdentity(keys->pool, idkey, cert);
if (identity == NULL)
{
return NULL;
}
/* Validate the public key from the certificate matches the identity key
* provided. */
if (verifyReadKeyPair(keys->pool, identity, keys->poolUserPtr) < PS_SUCCESS)
{
psTraceInfo("Cert parse success but material didn't validate\n");
matrixSslFreeIdentity(keys->pool, identity);
return NULL;
}
/* Push the new key at the end of the list, so that routines using the
keys can easily prefer the first one added. */
return matrixSslAddIdentity(keys, identity);
}
/* Load keypair from memory buffer to 'keys' */
static psRes_t sslLoadKeyPair(psPool_t *pool,
psPubKey_t *key,
int keytype,
const char *keypass,
const unsigned char *keydata,
psSizeL_t keydata_len)
{
psRes_t err = PS_SUCCESS;
# ifdef USE_PRIVATE_KEY_PARSING
unsigned char *unarmored;
psSizeL_t unarmored_len;
int32_t rc;
if (keydata == NULL || keydata_len == 0)
{
psTraceInfo("sslLoadKeyPair(): no key material");
key->type = PS_NOKEY;
key->keysize = 0;
return PS_SUCCESS;
}
rc = psPemTryDecode(pool,
keydata,
keydata_len,
PEM_TYPE_KEY,
keypass,
&unarmored,
&unarmored_len);
if (rc != PS_SUCCESS)
{
/* Not PEM or PEM decoding not supported. Try DER. */
unarmored = (unsigned char*)keydata;
unarmored_len = keydata_len;
}
psInitPubKey(pool, key, keytype);
switch (keytype)
{
# ifdef USE_RSA
case PS_RSA:
err = psRsaParsePkcs1PrivKey(pool,
unarmored,
unarmored_len,
&key->key.rsa);
if (err < 0)
{
# ifdef USE_PKCS8
/* Attempt a PKCS#8 but mem parse doesn't take password */
err = psPkcs8ParsePrivBin(pool,
unarmored,
unarmored_len,
NULL,
key);
if (err >= 0)
{
goto out;
}
# else
goto out;
# endif
}
key->keysize = psRsaSize(&key->key.rsa);
break;
# endif /* USE_RSA */
# ifdef USE_ECC
case PS_ECC:
err = psEccParsePrivKey(pool,
unarmored,
unarmored_len,
&key->key.ecc,
NULL);
if (err < 0)
{
# ifdef USE_PKCS8
/* Attempt a PKCS#8 but mem parse doesn't take password */
err = psPkcs8ParsePrivBin(pool,
unarmored,
unarmored_len,
NULL,
key);
if (err >= 0)
{
goto out;
}
# else
goto out;
# endif
}
key->keysize = psEccSize(&key->key.ecc);
break;
# endif /* USE_ECC */
# ifdef USE_ED25519
case PS_ED25519:
err = psEd25519ParsePrivKey(pool,
unarmored,
unarmored_len,
&key->key.ed25519);
if (err < 0)
{
goto out;
}
key->keysize = 32;
break;
# endif /* USE_ED25519 */
default:
psTraceErrr("Unknown key type in sslLoadKeyPair\n");
err = PS_UNSUPPORTED_FAIL;
break;
} /* end switch */
out:
if (unarmored != keydata)
psFree(unarmored, pool);
# endif
return err;
}
/* Load a X509 certificate (chain) from given PEM or binary DER encoded
data */
static psRes_t sslLoadCert(psPool_t *pool,
psX509Cert_t **cert,
const unsigned char *data,
psSizeL_t data_len,
matrixSslLoadKeysOpts_t *opts)
{
psRes_t rc = PS_SUCCESS;
*cert = NULL;
if (data == NULL || data_len == 0)
{
return PS_SUCCESS;
}
rc = psX509ParseCertData(pool,
data,
data_len,
cert,
(CERT_STORE_UNPARSED_BUFFER|CERT_STORE_DN_BUFFER));
if (rc < PS_SUCCESS)
{
psX509FreeCert((*cert));
return rc;
}
rc = checkAuthFailFlags(*cert, opts);
if (rc != PS_SUCCESS)
{
psX509FreeCert((*cert));
return rc;
}
return rc;
}
psRes_t psRotSetupIdentityKey(matrixSslLoadKeysOpts_t *opts,
psPubKey_t *idKey)
{
# ifdef USE_ROT_CRYPTO
int32_t err;
# ifdef USE_ROT_ECC
const psEccCurve_t *curve;
# endif
switch (opts->key_type)
{
# ifdef USE_ROT_ECC
case PS_ECC:
idKey->type = PS_ECC;
idKey->key.ecc.rotKeyType = ps_ecc_key_type_ecdsa;
idKey->key.ecc.privAsset = opts->privAsset;
psTraceIntInfo("Using ECDSA private key asset: %u\n",
idKey->key.ecc.privAsset);
err = getEccParamById(opts->privAssetCurveId,
&curve);
psTraceStrInfo(" (%s curve)\n", curve->name);
idKey->key.ecc.curve = curve;
if (err < 0)
{
psTraceErrr("getEccParamById failed\n");
return err;
}
idKey->keysize = curve->size * 2;
err = psRotLoadCurve(curve->curveId,
&idKey->key.ecc.domainAsset);
if (err < 0)
{
psTraceErrr("psRotLoadCurve failed\n");
return err;
}
/* Do not take ownership of this asset - we are not responsible
for freeing. */
idKey->key.ecc.longTermPrivAsset = PS_TRUE;
break;
# endif
# ifdef USE_ROT_RSA
case PS_RSA:
idKey->type = PS_RSA;
idKey->key.rsa.privSigAsset = opts->privAsset;
psTraceIntInfo("Using RSA private key asset: %u\n",
idKey->key.rsa.privSigAsset);
idKey->keysize = opts->privAssetModulusNBytes;
idKey->key.rsa.size = opts->privAssetModulusNBytes;
/* Do not take ownership of this asset - we are not responsible
for freeing. */
idKey->key.rsa.longTermPrivAsset = PS_TRUE;
break;
# endif
default:
psTraceErrr("Unknown key type in psRotSetupIdentityKey\n");
return PS_UNSUPPORTED_FAIL;
}
return PS_SUCCESS;
# else
(void)opts;
(void)idKey;
psTraceErrr("USE_ROT_CRYPTO needed for psRotSetupIdentityKey\n");
return PS_UNSUPPORTED_FAIL;
# endif /* USE_ROT_CRYPTO */
}
psRes_t
matrixSslCreateIdentityFromData(sslKeys_t *keys,
const unsigned char *cert,
psSizeL_t cert_len,
int32 keytype,
const char *keypass,
const unsigned char *keydata,
psSizeL_t keydata_len,
matrixSslLoadKeysOpts_t *opts)
{
psPubKey_t idkey;
psX509Cert_t *idcert = NULL;
int32 err;
sslIdentity_t *id;
psRes_t rc;
memset(&idkey, 0, sizeof(idkey));
Memset(&idkey, 0, sizeof idkey); /* Zeroize idkey. */
err = sslLoadCert(keys->pool, &idcert, cert, cert_len, opts);
if (err < PS_SUCCESS)
{
return err;
}
if (opts && opts->privAsset != 0)
{
rc = psRotSetupIdentityKey(opts, &idkey);
if (rc < 0)
{
psTraceErrr("psRotLoadIdentity failed\n");
psX509FreeCert(idcert);
return rc;
}
}
else
{
err = sslLoadKeyPair(keys->pool,
&idkey,
keytype,
keypass,
keydata,
keydata_len);
if (err < PS_SUCCESS)
{
psX509FreeCert(idcert);
return err;
}
}
id = matrixSslCreateIdentity(keys, idkey, idcert);
if (id == NULL)
{
return PS_FAILURE;
}
return PS_SUCCESS;
}
static psRes_t matrixSslLoadKeyMaterialMem(sslKeys_t *keys,
const unsigned char *certBuf,
int32 certLen,
const unsigned char *privBuf,
int32 privLen,
const unsigned char *CAbuf,
int32 CAlen,
int32 privKeyType,
matrixSslLoadKeysOpts_t *opts)
{
psRes_t err;
if (keys == NULL
|| (certBuf == NULL && privBuf == NULL && CAbuf == NULL))
{
return PS_ARG_FAIL;
}
# ifdef USE_ROT_CRYPTO
if (privKeyType != PS_ECC && privKeyType != PS_RSA)
{
psTraceErrr("Only ECDSA/RSA auth keys supported by crypto-rot\n");
return PS_ARG_FAIL;
}
if (opts && opts->privAsset != VAL_ASSETID_INVALID)
{
/* The asset ID overrides the plaintext key. */
privBuf = NULL;
privLen = 0;
}
# endif /* USE_ROT_CRYPTO */
err = matrixSslAddTrustAnchors(keys, NULL, CAbuf, CAlen, opts);
if (err < PS_SUCCESS)
{
return err;
}
if ((privBuf != NULL && certBuf != NULL) ||
(opts != NULL && opts->privAsset != 0))
{
err = matrixSslCreateIdentityFromData(keys,
certBuf,
certLen,
privKeyType,
NULL,
privBuf,
privLen,
opts);
if (err < PS_SUCCESS)
{
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
psX509FreeCert(keys->CAcerts);
keys->CAcerts = NULL;
# endif
return PS_CERT_AUTH_FAIL;
}
}
return PS_SUCCESS;
}
int32_t matrixSslLoadKeysMem(sslKeys_t *keys,
const unsigned char *certBuf,
int32 certLen,
const unsigned char *privBuf,
int32 privLen,
const unsigned char *CAbuf,
int32 CAlen,
matrixSslLoadKeysOpts_t *opts)
{
int32_t keytype = 0;
int32_t rc = PS_FAILURE;
if (opts)
{
keytype = opts->key_type;
}
if (privBuf == NULL)
{
keytype = 1;
}
/* Note: previous versions used the constants 1 for RSA,
2 for ECC and 3 for EDDSA. */
switch (keytype)
{
case PS_RSA:
rc = matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_RSA,
opts);
break;
case PS_ECC:
rc = matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_ECC,
opts);
break;
# ifdef USE_ED25519
case PS_ED25519:
rc = matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_ED25519,
opts);
break;
# endif /* USE_ED25519 */
case 0:
{
int32 try[] = { PS_RSA, PS_ECC,
# ifdef USE_ED25519
PS_ED25519,
# endif /* USE_ED25519 */
-1};
int32 i;
for (i = 0; try[i] != -1; i++)
{
rc = matrixSslLoadKeyMaterialMem(
keys,
certBuf, certLen, privBuf, privLen,
NULL, 0,
try[i], opts);
if (rc == PS_SUCCESS)
{
break;
}
}
if (rc == PS_SUCCESS && CAbuf && CAlen > 0)
{
rc = matrixSslLoadKeyMaterialMem(
keys, NULL, 0, NULL, 0,
CAbuf, CAlen, 0, opts);
}
}
break;
default:
/* Unknown key type */
psTraceErrr("Unknown key type in matrixSslLoadKeysMem\n");
rc = PS_FAILURE;
}
return rc;
}
#ifdef USE_RSA
int32 matrixSslLoadRsaKeysMemExt(sslKeys_t *keys,
const unsigned char *certBuf,
int32 certLen,
const unsigned char *privBuf,
int32 privLen,
const unsigned char *CAbuf,
int32 CAlen,
matrixSslLoadKeysOpts_t *opts)
{
return matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_RSA,
opts);
}
int32 matrixSslLoadRsaKeysMem(sslKeys_t *keys,
const unsigned char *certBuf,
int32 certLen,
const unsigned char *privBuf,
int32 privLen,
const unsigned char *CAbuf,
int32 CAlen)
{
return matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_RSA,
NULL);
}
#endif /* USE_RSA */
#ifdef USE_ECC
int32 matrixSslLoadEcKeysMemExt(sslKeys_t *keys,
const unsigned char *certBuf,
int32 certLen,
const unsigned char *privBuf,
int32 privLen,
const unsigned char *CAbuf,
int32 CAlen,
matrixSslLoadKeysOpts_t *opts)
{
return matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_ECC,
opts);
}
int32 matrixSslLoadEcKeysMem(sslKeys_t *keys,
const unsigned char *certBuf,
int32 certLen,
const unsigned char *privBuf,
int32 privLen,
const unsigned char *CAbuf,
int32 CAlen)
{
return matrixSslLoadKeyMaterialMem(keys,
certBuf,
certLen,
privBuf,
privLen,
CAbuf,
CAlen,
PS_ECC,
NULL);
}
#endif /* USE_ECC */
#ifdef USE_PKCS12
int32 matrixSslLoadPkcs12Mem(sslKeys_t *keys,
const unsigned char *p12Buf,
int32 p12Len,
const unsigned char *importPass,
int32 ipasslen,
const unsigned char *macPass,
int32 mpasslen,
int32 flags)
{
unsigned char *mPass;
psPool_t *pool;
int32 rc;
psX509Cert_t *cert;
psPubKey_t idkey;
sslIdentity_t *id;
Memset(&idkey, 0, sizeof idkey); /* Zeroize idkey. */
if (keys == NULL)
{
return PS_ARG_FAIL;
}
pool = keys->pool;
PS_POOL_USED(pool);
Memset(&idkey, 0, sizeof(idkey));
if (macPass == NULL)
{
mPass = (unsigned char *) importPass;
mpasslen = ipasslen;
}
else
{
mPass = (unsigned char *) macPass;
}
rc = psPkcs12ParseMem(pool,
&cert,
&idkey,
p12Buf,
p12Len,
flags,
(unsigned char *) importPass,
ipasslen,
mPass,
mpasslen);
if (rc < 0)
{
psX509FreeCert(cert);
psClearPubKey(&idkey);
return rc;
}
id = matrixSslCreateIdentity(keys, idkey, cert);
if (id == NULL)
{
return PS_FAILURE;
}
return PS_SUCCESS;
}
# endif /* USE_PKCS12 */
#endif /* USE_IDENTITY_CERTIFICATES */
#ifdef REQUIRE_DH_PARAMS
int32 matrixSslLoadDhParamsMem(sslKeys_t *keys,
const unsigned char *dhBin,
int32 dhBinLen)
{
if (keys == NULL)
{
return PS_ARG_FAIL;
}
return psPkcs3ParseDhParamBin(keys->pool,
(unsigned char *) dhBin,
dhBinLen,
&keys->dhParams);
}
#endif /* REQUIRE_DH_PARAMS */
#ifdef USE_ECC
/* User is specifying EC curves that are supported so check that against the
keys they are supporting */
static struct {
int curveId;
int flag;
} matrixCurveIdFlag[] = {
{ 19, IS_SECP192R1 },
{ 21, IS_SECP224R1 },
{ 23, IS_SECP256R1 },
{ 24, IS_SECP384R1 },
{ 25, IS_SECP521R1 },
{ 26, IS_BRAIN256R1 },
{ 27, IS_BRAIN384R1 },
{ 28, IS_BRAIN512R1 },
{ 255, IS_BRAIN224R1 },
{ 41, IS_CURVESM2 },
{ 0, 0 }
};
int32 curveIdToFlag(int32 id)
{
int i;
for (i = 0; i < sizeof(matrixCurveIdFlag)/ sizeof(matrixCurveIdFlag[0]); i++)
{
if (matrixCurveIdFlag[i].curveId == id)
return matrixCurveIdFlag[i].flag;
}
return 0;
}
psRes_t psTestUserEcID(int32 id, int32 ecFlags)
{
int i;
for (i = 0; i < sizeof(matrixCurveIdFlag)/ sizeof(matrixCurveIdFlag[0]); i++)
{
if (matrixCurveIdFlag[i].curveId == id)
{
if (!(ecFlags & matrixCurveIdFlag[i].flag))
return PS_FAILURE;
else
return PS_SUCCESS;
}
}
return PS_UNSUPPORTED_FAIL;
}
int32 psTestUserEc(int32 ecFlags, const sslKeys_t *keys)
{
const psEccKey_t *eccKey;
int goodEccCount = 0;
int otherKeyCount = 0;
psRes_t res;
# ifdef USE_CERT_PARSE
psX509Cert_t *cert;
# endif /* USE_CERT_PARSE */
# ifdef USE_IDENTITY_CERTIFICATES
sslIdentity_t *p;
for (p = keys->identity; p; p = p->next)
{
if (p->privKey.type != PS_ECC)
{
otherKeyCount++;
continue;
}
eccKey = &p->privKey.key.ecc;
res = psTestUserEcID(eccKey->curve->curveId, ecFlags);
if (res < 0)
{
continue;
}
# ifdef USE_CERT_PARSE
for (cert = p->cert; cert; cert = cert->next)
{
if (cert->publicKey.type == PS_ECC)
{
eccKey = &cert->publicKey.key.ecc;
res = psTestUserEcID(eccKey->curve->curveId, ecFlags);
if (res < 0)
{
break;
}
}
}
if (p->cert && !cert)
{
/* had certs, and did not break out from the loop above -
something fruitful */
goodEccCount++;
}
# else /* USE_CERT_PARSE */
goodEccCount++;
# endif /* USE_CERT_PARSE */
}
# endif /* USE_IDENTITY_CERTIFICATES */
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
# ifdef USE_CERT_PARSE
for (cert = keys->CAcerts; cert != NULL; cert = cert->next)
{
if (cert->publicKey.type == PS_ECC)
{
eccKey = &cert->publicKey.key.ecc;
res = PS_FAILURE;
if (eccKey->curve)
{
res = psTestUserEcID(eccKey->curve->curveId, ecFlags);
}
if (res == PS_SUCCESS)
{
goodEccCount++;
}
}
}
# endif /* USE_CERT_PARSE */
# endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
return goodEccCount > 0 || otherKeyCount > 0;
}
/**
Generate and cache an ephemeral ECC key for later use in ECDHE key exchange.
@param[out] keys Keys structure to hold ephemeral keys
@param[in] curve ECC curve to generate key on, or NULL to generate for all
supported curves.
@param[in] hwCtx Context for hardware crypto.
*/
int32_t matrixSslGenEphemeralEcKey(sslKeys_t *keys,
psEccKey_t *ecc,
const psEccCurve_t *curve,
void *hwCtx)
{
# if ECC_EPHEMERAL_CACHE_USAGE > 0
psTime_t t;
# endif
int32_t rc = PS_FAILURE;
if (keys == NULL || curve == NULL)
{
return PS_ARG_FAIL;
}
# if ECC_EPHEMERAL_CACHE_USAGE > 0
psGetTime(&t, keys->poolUserPtr);
psLockMutex(&keys->cache.lock);
if (keys->cache.eccPrivKey.curve != curve)
{
psTraceStrInfo("Generating ephemeral %s key (new curve)\n",
curve->name);
goto L_REGEN;
}
if (keys->cache.eccPrivKeyUse > ECC_EPHEMERAL_CACHE_USAGE)
{
psTraceStrInfo("Generating ephemeral %s key (usage exceeded)\n",
curve->name);
goto L_REGEN;
}
if (psDiffMsecs(keys->cache.eccPrivKeyTime, t, keys->poolUserPtr) >
(1000 * ECC_EPHEMERAL_CACHE_SECONDS))
{
psTraceStrInfo("Generating ephemeral %s key (time exceeded)\n",
curve->name);
goto L_REGEN;
}
keys->cache.eccPrivKeyUse++;
rc = PS_SUCCESS;
if (ecc)
{
rc = psEccCopyKey(ecc, &keys->cache.eccPrivKey);
}
psUnlockMutex(&keys->cache.lock);
return rc;
L_REGEN:
if (keys->cache.eccPrivKeyUse)
{
/* We use eccPrivKeyUse == 0 as a flag to note the key not allocated */
psEccClearKey(&keys->cache.eccPrivKey);
keys->cache.eccPrivKeyUse = 0;
}
# ifdef USE_ROT_ECC
keys->cache.eccPrivKey.rotKeyType = ps_ecc_key_type_ecdhe;
# endif
rc = psEccGenKey(keys->pool, &keys->cache.eccPrivKey, curve, hwCtx);
if (rc < 0)
{
psUnlockMutex(&keys->cache.lock);
return rc;
}
keys->cache.eccPrivKeyTime = t;
keys->cache.eccPrivKeyUse = 1;
rc = PS_SUCCESS;
if (ecc)
{
rc = psEccCopyKey(ecc, &keys->cache.eccPrivKey);
}
psUnlockMutex(&keys->cache.lock);
return rc;
# else
/* Not using ephemeral caching. */
if (ecc)
{
# ifdef USE_ROT_ECC
ecc->rotKeyType = ps_ecc_key_type_ecdhe;
# endif
psTraceStrInfo("Generating ephemeral %s key (new curve)\n",
curve->name);
rc = psEccGenKey(keys->pool, ecc, curve, hwCtx);
return rc;
}
rc = PS_SUCCESS;
return rc;
# endif /* ECC_EPHEMERAL_CACHE_USAGE > 0 */
}
#endif /* USE_ECC */
#ifdef MATRIX_USE_FILE_SYSTEM
# ifdef USE_PKCS12
/******************************************************************************/
/*
File should be a binary .p12 or .pfx
*/
int32 matrixSslLoadPkcs12(sslKeys_t *keys,
const unsigned char *certFile,
const unsigned char *importPass,
int32 ipasslen,
const unsigned char *macPass,
int32 mpasslen,
int32 flags)
{
unsigned char *mPass;
psPool_t *pool;
int32 rc;
psX509Cert_t *cert;
psPubKey_t idkey;
sslIdentity_t *id;
if (keys == NULL)
{
return PS_ARG_FAIL;
}
pool = keys->pool;
PS_POOL_USED(pool);
Memset(&idkey, 0, sizeof(idkey));
if (macPass == NULL)
{
mPass = (unsigned char *) importPass;
mpasslen = ipasslen;
}
else
{
mPass = (unsigned char *) macPass;
}
rc = psPkcs12Parse(pool,
&cert,
&idkey,
certFile,
flags,
(unsigned char *) importPass,
ipasslen,
mPass,
mpasslen);
if (rc < 0)
{
if (cert)
{
psX509FreeCert(cert);
}
psClearPubKey(&idkey);
return rc;
}
id = matrixSslCreateIdentity(keys, idkey, cert);
if (id == NULL)
{
return PS_FAILURE;
}
return PS_SUCCESS;
}
/******************************************************************************/
# endif /* USE_PKCS12 */
# if defined(USE_RSA) || defined(USE_ECC)
static psRes_t matrixSslLoadKeyMaterial(sslKeys_t *keys,
const char *certFile,
const char *privFile,
const char *privPass,
const char *CAfile,
int32 privKeyType,
matrixSslLoadKeysOpts_t *opts)
{
int32 err = PS_UNSUPPORTED_FAIL;
if (keys == NULL)
{
return PS_ARG_FAIL;
}
err = matrixSslAddTrustAnchors(keys, CAfile, NULL, 0, opts);
if (err < PS_SUCCESS)
{
return err;
}
# if defined(USE_IDENTITY_CERTIFICATES)
if (privFile && certFile)
{
unsigned char *idkey, *cert;
psSizeL_t idkey_len, cert_len;
psPool_t *pool = keys->pool;
err = psGetFileBuf(pool, privFile, &idkey, &idkey_len);
if (err == PS_SUCCESS)
{
err = psGetFileBuf(pool, certFile, &cert, &cert_len);
if (err == PS_SUCCESS)
{
err = matrixSslCreateIdentityFromData(keys,
cert,
cert_len,
privKeyType,
privPass,
idkey,
idkey_len,
opts);
psFree(cert, pool);
}
psFree(idkey, pool);
}
}
# endif /* USE_IDENTITY_CERTIFICATES */
return err;
}
#endif /* USE_RSA || USE_ECC */
# ifdef USE_RSA
/******************************************************************************/
PSPUBLIC int32 matrixSslLoadRsaKeysExt(sslKeys_t *keys,
const char *certFile,
const char *privFile,
const char *privPass,
const char *trustedCAFile,
matrixSslLoadKeysOpts_t *opts)
{
return matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
trustedCAFile,
PS_RSA,
opts);
}
int32 matrixSslLoadRsaKeys(sslKeys_t *keys,
const char *certFile,
const char *privFile,
const char *privPass,
const char *CAfile)
{
return matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
CAfile,
PS_RSA,
NULL);
}
# endif /* USE_RSA */
# ifdef USE_ECC
/******************************************************************************/
int32 matrixSslLoadEcKeysExt(sslKeys_t *keys,
const char *certFile,
const char *privFile,
const char *privPass,
const char *CAfile,
matrixSslLoadKeysOpts_t *opts)
{
return matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
CAfile,
PS_ECC,
opts);
}
int32 matrixSslLoadEcKeys(sslKeys_t *keys,
const char *certFile,
const char *privFile,
const char *privPass,
const char *CAfile)
{
return matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
CAfile,
PS_ECC,
NULL);
}
# endif /* USE_ECC */
# if defined(USE_IDENTITY_CERTIFICATES)
psRes_t matrixSslLoadKeys(sslKeys_t *keys,
const char *certFile,
const char *privFile,
const char *privPass,
const char *CAfile,
matrixSslLoadKeysOpts_t *opts)
{
int32_t keytype = 0;
int32_t rc = PS_FAILURE;
if (opts)
{
keytype = opts->key_type;
}
# ifdef USE_ALWAYS_ALLOW_OUT_OF_DATE_CERT_PARSE
opts.flags |= LOAD_KEYS_OPT_ALLOW_OUT_OF_DATE_CERT_PARSE;
# endif
if (privFile == NULL)
{
keytype = 1;
}
switch (keytype)
{
case PS_RSA:
rc = matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
CAfile,
PS_RSA,
opts);
break;
case PS_ECC:
rc = matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
CAfile,
PS_ECC,
opts);
break;
# ifdef USE_ED25519
case PS_ED25519:
rc = matrixSslLoadKeyMaterial(keys,
certFile,
privFile,
privPass,
CAfile,
PS_ED25519,
opts);
break;
# endif /* USE_ED25519 */
case 0:
{
int32 try[] = { PS_RSA, PS_ECC,
# ifdef USE_ED25519
PS_ED25519,
# endif /* USE_ED25519 */
-1};
int32 i;
for (i = 0; try[i] != -1; i++)
{
rc = matrixSslLoadKeyMaterial(
keys,
certFile, privFile, privPass,
NULL, try[i], opts);
if (rc == PS_SUCCESS)
{
break;
}
}
if (rc == PS_SUCCESS && CAfile)
{
rc = matrixSslLoadKeyMaterial(
keys, NULL, NULL, NULL, CAfile, 0, opts);
}
}
break;
default:
psTraceErrr("Unknown key type in matrixSslLoadKeys\n");
rc = PS_FAILURE;
break;
}
return rc;
}
# endif
# ifdef REQUIRE_DH_PARAMS
/******************************************************************************/
/*
User level API to assign the DH parameter file to the server application.
*/
int32 matrixSslLoadDhParams(sslKeys_t *keys, const char *paramFile)
{
if (keys == NULL)
{
return PS_ARG_FAIL;
}
return psPkcs3ParseDhParamFile(keys->pool, (char *) paramFile, &keys->dhParams);
}
# endif /* REQUIRE_DH_PARAMS */
#endif /* MATRIX_USE_FILE_SYSTEM */
#if defined(USE_OCSP_RESPONSE) && defined(USE_SERVER_SIDE_SSL)
int32_t matrixSslLoadOCSPResponse(sslKeys_t *keys,
const unsigned char *OCSPResponseBuf, psSize_t OCSPResponseBufLen)
{
psPool_t *pool;
if (keys == NULL || OCSPResponseBuf == NULL || OCSPResponseBufLen == 0)
{
return PS_ARG_FAIL;
}
pool = keys->pool;
PS_POOL_USED(pool);
/* Overwrite/Update any response being set */
if (keys->OCSPResponseBuf != NULL)
{
psFree(keys->OCSPResponseBuf, pool);
keys->OCSPResponseBufLen = 0;
}
keys->OCSPResponseBufLen = OCSPResponseBufLen;
if ((keys->OCSPResponseBuf = psMalloc(pool, OCSPResponseBufLen)) == NULL)
{
return PS_MEM_FAIL;
}
Memcpy(keys->OCSPResponseBuf, OCSPResponseBuf, OCSPResponseBufLen);
return PS_SUCCESS;
}
#endif /* USE_OCSP_RESPONSE && USE_SERVER_SIDE_SSL */
/******************************************************************************/
/******************************************************************************/
/*
Must call to allocate the key structure now. After which, LoadRsaKeys,
LoadDhParams and/or LoadPskKey can be called
Memory info:
Caller must free keys with matrixSslDeleteKeys on function success
Caller does not need to free keys on function failure
*/
int32_t matrixSslNewKeys(sslKeys_t **keys, void *memAllocUserPtr)
{
psPool_t *pool = NULL;
sslKeys_t *lkeys;
#if defined(USE_ECC) || defined(REQUIRE_DH_PARAMS)
int32_t rc;
#endif
lkeys = psMalloc(pool, sizeof(sslKeys_t));
if (lkeys == NULL)
{
return PS_MEM_FAIL;
}
Memset(lkeys, 0x0, sizeof(sslKeys_t));
lkeys->pool = pool;
lkeys->poolUserPtr = memAllocUserPtr;
#if defined(USE_ECC) || defined(REQUIRE_DH_PARAMS)
rc = psCreateMutex(&lkeys->cache.lock, 0);
if (rc < 0)
{
psFree(lkeys, pool);
return rc;
}
#endif
*keys = lkeys;
return PS_SUCCESS;
}
/******************************************************************************/
/*
This will free the struct and any key material that was loaded via:
matrixSslLoadRsaKeys
matrixSslLoadEcKeys
matrixSslLoadDhParams
matrixSslLoadPsk
matrixSslLoadOCSPResponse
*/
void matrixSslDeleteKeys(sslKeys_t *keys)
{
#ifdef USE_PSK_CIPHER_SUITE
psPsk_t *psk, *next;
#endif /* USE_PSK_CIPHER_SUITE */
#if defined(USE_STATELESS_SESSION_TICKETS) && defined(USE_SERVER_SIDE_SSL)
psSessionTicketKeys_t *tick, *nextTick;
#endif
if (keys == NULL)
{
return;
}
# ifdef USE_IDENTITY_CERTIFICATES
{
sslIdentity_t *p, *next;
for (p = keys->identity; p != NULL; p = next)
{
next = p->next;
psX509FreeCert(p->cert);
psClearPubKey(&p->privKey);
psFree(p, keys->pool);
}
}
# endif /* USE_IDENTITY_CERTIFICATES */
# ifndef USE_ONLY_PSK_CIPHER_SUITE
# if defined(USE_CLIENT_SIDE_SSL) || defined(USE_CLIENT_AUTH)
if (keys->CAcerts)
{
psX509FreeCert(keys->CAcerts);
}
# endif /* USE_CLIENT_SIDE_SSL || USE_CLIENT_AUTH */
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
#ifdef REQUIRE_DH_PARAMS
psPkcs3ClearDhParams(&keys->dhParams);
#endif /* REQUIRE_DH_PARAMS */
#ifdef USE_PSK_CIPHER_SUITE
if (keys->pskKeys)
{
psk = keys->pskKeys;
while (psk)
{
psFree(psk->pskKey, keys->pool);
psFree(psk->pskId, keys->pool);
next = psk->next;
psFree(psk, keys->pool);
psk = next;
}
}
#endif /* USE_PSK_CIPHER_SUITE */
#ifdef USE_TLS_1_3
tls13FreePsk(keys->tls13PskKeys, keys->pool);
#endif /* USE_TLS_1_3 */
#if defined(USE_STATELESS_SESSION_TICKETS) && defined(USE_SERVER_SIDE_SSL)
if (keys->sessTickets)
{
tick = keys->sessTickets;
while (tick)
{
nextTick = tick->next;
psFree(tick, keys->pool);
tick = nextTick;
}
}
#endif
#if defined(USE_ECC) || defined(REQUIRE_DH_PARAMS)
psDestroyMutex(&keys->cache.lock);
# ifdef USE_ECC
if (keys->cache.eccPrivKeyUse > 0)
{
psEccClearKey(&keys->cache.eccPrivKey);
psEccClearKey(&keys->cache.eccPubKey);
}
# endif
/* Remainder of structure is cleared below */
#endif
#if defined(USE_OCSP_RESPONSE) && defined(USE_SERVER_SIDE_SSL)
if (keys->OCSPResponseBuf != NULL)
{
psFree(keys->OCSPResponseBuf, keys->pool);
keys->OCSPResponseBufLen = 0;
}
#endif
memzero_s(keys, sizeof(sslKeys_t));
psFree(keys, NULL);
}