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

394 lines
12 KiB
C

/**
* @file pem_decode_mem.c
* @version $Format:%h%d$
*
* Functions for in-memory PEM decoding.
*/
/*
* 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
*/
/******************************************************************************/
#include "../cryptoImpl.h"
# ifdef USE_PRIVATE_KEY_PARSING
int32_t psPemTryDecode(psPool_t *pool,
const unsigned char *in,
psSizeL_t inLen,
psPemType_t pemType,
const char *password,
unsigned char **out,
psSizeL_t *outLen)
{
# ifndef USE_PEM_DECODE
return PS_UNSUPPORTED_FAIL;
# else
psBool_t isPem = PS_FALSE;
int32_t rc;
isPem = psPemCheckOk(in,
inLen,
pemType,
NULL,
NULL,
NULL);
if (isPem)
{
rc = psPemDecode(pool,
in,
inLen,
password,
out,
outLen);
if (rc != PS_SUCCESS)
{
return rc;
}
return PS_SUCCESS;
}
return PS_ARG_FAIL; /* Input not PEM. */
# endif /* USE_PEM_DECODE */
}
# endif /* USE_PRIVATE_KEY_PARSING */
# ifdef USE_PEM_DECODE
psBool_t psPemCheckOk(const unsigned char *pemBuf,
psSizeL_t pemBufLen,
psPemType_t pemType,
char **startp,
char **endp,
psSizeL_t *pemlen)
{
char *start, *end;
/* Check header and encryption parameters. */
if (((start = Strstr((char *) pemBuf, "-----BEGIN")) != NULL) &&
((start = Strstr((char *) pemBuf, "PRIVATE KEY-----")) != NULL) &&
((end = Strstr(start, "-----END")) != NULL) &&
(Strstr(end, "PRIVATE KEY-----") != NULL))
{
if (pemType != PEM_TYPE_KEY &&
pemType != PEM_TYPE_PRIVATE_KEY &&
pemType != PEM_TYPE_ANY)
{
return PS_FALSE;
}
start += Strlen("PRIVATE KEY-----");
while (*start == '\x0d' || *start == '\x0a')
{
start++;
}
}
else if (((start = Strstr((char *) pemBuf, "-----BEGIN")) != NULL) &&
((start = Strstr((char *) pemBuf, "PUBLIC KEY-----")) != NULL) &&
((end = Strstr(start, "-----END")) != NULL) &&
(Strstr(end, "PUBLIC KEY-----") != NULL))
{
if (pemType != PEM_TYPE_PUBLIC_KEY &&
pemType != PEM_TYPE_KEY &&
pemType != PEM_TYPE_ANY)
{
return PS_FALSE;
}
start += Strlen("PUBLIC KEY-----");
while (*start == '\x0d' || *start == '\x0a')
{
start++;
}
}
else if (((start = Strstr((char *) pemBuf, "-----BEGIN")) != NULL) &&
((start = Strstr((char *) pemBuf, "CERTIFICATE-----")) != NULL) &&
((end = Strstr(start, "-----END")) != NULL) &&
(Strstr(end, "CERTIFICATE-----") != NULL))
{
if (pemType != PEM_TYPE_CERTIFICATE &&
pemType != PEM_TYPE_ANY)
{
return PS_FALSE;
}
start += Strlen("CERTIFICATE-----");
while (*start == '\x0d' || *start == '\x0a')
{
start++;
}
}
else
{
return PS_FALSE;
}
if (pemlen != NULL)
{
*pemlen = (psSizeL_t) (end - start);
}
if (startp != NULL && endp != NULL)
{
*startp = start;
*endp = end;
}
return PS_TRUE;
}
int32_t psPemDecode(psPool_t *pool,
const unsigned char *keyBufIn,
psSizeL_t keyBufLen,
const char *password,
unsigned char **out,
psSizeL_t *outlen)
{
# if defined(USE_PKCS5) && defined(USE_PBKDF1)
psDes3_t dctx;
psAesCbc_t actx;
unsigned char passKey[32]; /* AES-256 max */
unsigned char cipherIV[16]; /* AES-256 max */
int32 tmp, encrypted = 0;
static const char des3encryptHeader[] = "DEK-Info: DES-EDE3-CBC,";
static const char aes128encryptHeader[] = "DEK-Info: AES-128-CBC,";
# endif /* USE_PKCS5 && USE_PBKDF1 */
unsigned char *dout;
char *start, *end;
int32 rc;
psSizeL_t PEMlen = 0;
const char *keyBuf;
psSize_t outlenPsSize;
start = end = NULL;
if (!psPemCheckOk(keyBufIn,
keyBufLen,
PEM_TYPE_ANY,
&start,
&end,
&PEMlen))
{
psTraceCrypto("Input does not look to be in PKCS#1 PEM format\n");
return PS_PARSE_FAIL;
}
keyBuf = (const char *)keyBufIn;
if (Strstr((char *) keyBuf, "Proc-Type:") &&
Strstr((char *) keyBuf, "4,ENCRYPTED"))
{
# if defined(USE_PKCS5) && defined(USE_PBKDF1)
if (password == NULL)
{
psTraceCrypto("No password given for encrypted private key file\n");
return PS_ARG_FAIL;
}
if ((start = Strstr((char *) keyBuf, des3encryptHeader)) != NULL)
{
start += Strlen(des3encryptHeader);
encrypted = 1;
/* we assume here that header points to at least 16 bytes of data */
tmp = psHexToBinary((unsigned char *) start, cipherIV, DES3_IVLEN);
}
else if ((start = Strstr((char *) keyBuf, aes128encryptHeader))
!= NULL)
{
start += Strlen(aes128encryptHeader);
encrypted = 2;
/* we assume here that header points to at least 32 bytes of data */
tmp = psHexToBinary((unsigned char *) start, cipherIV, 16);
}
else
{
psTraceCrypto("Unrecognized private key file encoding\n");
return PS_PARSE_FAIL;
}
if (tmp < 0)
{
psTraceCrypto("Invalid private key file salt\n");
return PS_FAILURE;
}
start += tmp;
if (psPkcs5Pbkdf1((unsigned char *) password, Strlen(password),
cipherIV, 1, (unsigned char *) passKey) < 0)
{
psTraceCrypto("psPkcs5Pbkdf1 failed\n");
return PS_FAILURE;
}
PEMlen = (int32) (end - start);
# else /* !USE_PKCS5 || !USE_PBKDF1 */
/* The private key is encrypted, but PKCS5 support has been turned off */
# ifndef USE_PKCS5
psTraceCrypto("USE_PKCS5 must be enabled for key file password\n");
# endif /* USE_PKCS5 */
# ifndef USE_PBKDF1
psTraceCrypto("USE_PBKDF1 must be enabled for key file password\n");
# endif /* USE_PBKDF1 */
return PS_UNSUPPORTED_FAIL;
# endif /* USE_PKCS5 && USE_PBKDF1 */
}
/* Take the raw input and do a base64 decode */
dout = psMalloc(pool, PEMlen);
if (dout == NULL)
{
psError("Memory allocation error in psPkcs1DecodePrivFile\n");
return PS_MEM_FAIL;
}
*outlen = PEMlen;
outlenPsSize = PEMlen;
rc = psBase64decode((unsigned char *) start,
PEMlen,
dout,
&outlenPsSize);
if (rc < 0)
{
psTraceCrypto("Error base64 decode of private key\n");
if (password)
{
psTraceCrypto("Is it possible the password is incorrect?\n");
}
psFree(dout, pool);
return rc;
}
*outlen = outlenPsSize;
# if defined(USE_PKCS5) && defined(USE_PBKDF1)
if (encrypted == 1 && password)
{
psDes3Init(&dctx, cipherIV, passKey);
psDes3Decrypt(&dctx, dout, dout, *outlen);
memset_s(&dctx, sizeof(psDes3_t), 0x0, sizeof(psDes3_t));
}
if (encrypted == 2 && password)
{
/* AES 128 */
psAesInitCBC(&actx, cipherIV, passKey, 16, PS_AES_DECRYPT);
psAesDecryptCBC(&actx, dout, dout, *outlen);
memset_s(&actx, sizeof(psAesCbc_t), 0x0, sizeof(psAesCbc_t));
}
/* SECURITY - zero out keys when finished */
memset_s(passKey, sizeof(passKey), 0x0, sizeof(passKey));
# endif /* USE_PKCS5 && USE_PBKDF1 */
*out = dout;
return PS_SUCCESS;
}
# ifdef USE_X509
psRes_t psPemCertBufToList(psPool_t *pool,
const unsigned char *buf,
psSizeL_t len,
psList_t **x509certList)
{
psList_t *front, *prev, *current;
unsigned char *start, *end, *endTmp;
const unsigned char *chFileBuf;
unsigned char l;
int n = 0;
int32_t rc;
*x509certList = NULL;
prev = NULL;
if (buf == NULL)
{
psTraceCrypto("Bad parameters to pemCertBufToList\n");
return PS_ARG_FAIL;
}
front = current = psMalloc(pool, sizeof(psList_t));
if (current == NULL)
{
psError("Memory allocation error first pemCertBufToList\n");
return PS_MEM_FAIL;
}
l = Strlen("CERTIFICATE-----");
Memset(current, 0x0, sizeof(psList_t));
chFileBuf = buf;
while (len > 0)
{
if (
((start = (unsigned char *) Strstr((char *) chFileBuf, "-----BEGIN")) != NULL) &&
((start = (unsigned char *) Strstr((char *) chFileBuf, "CERTIFICATE-----")) != NULL) &&
((end = (unsigned char *) Strstr((char *) start, "-----END")) != NULL) &&
((endTmp = (unsigned char *) Strstr((char *) end, "CERTIFICATE-----")) != NULL)
)
{
n++;
start += l;
if (current == NULL)
{
current = psMalloc(pool, sizeof(psList_t));
if (current == NULL)
{
psFreeList(front, pool);
psError("Memory allocation error: pemCertBufToList\n");
return PS_MEM_FAIL;
}
Memset(current, 0x0, sizeof(psList_t));
prev->next = current;
}
current->len = (uint16_t) (end - start);
end = endTmp + l;
while (*end == '\x0d' || *end == '\x0a' || *end == '\x09'
|| *end == ' ')
{
end++;
}
}
else
{
psFreeList(front, pool);
return PS_PARSE_FAIL;
}
current->item = psMalloc(pool, current->len);
if (current->item == NULL)
{
psFreeList(front, pool);
psError("Memory allocation error: pemCertBufToList\n");
return PS_MEM_FAIL;
}
Memset(current->item, '\0', current->len);
len -= (psSizeL_t) (end - buf);
buf = end;
rc = psBase64decode(start,
current->len,
current->item,
&current->len);
if (rc != 0)
{
psFreeList(front, pool);
psTraceCrypto("Unable to base64 decode certificate\n");
return PS_PARSE_FAIL;
}
prev = current;
current = current->next;
chFileBuf = buf;
}
*x509certList = front;
return PS_SUCCESS;
}
# endif /* USE_X509 */
# endif /* USE_PEM_DECODE */