Source code

Revision control

Copy as Markdown

Other Tools

/* tlsprf.c - TLS Pseudo Random Function (PRF) implementation
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "pkcs11i.h"
#include "blapi.h"
#include "secerr.h"
static void
sftk_TLSPRFNull(void *data, PRBool freeit)
{
return;
}
typedef struct {
PRUint32 cxSize; /* size of allocated block, in bytes. */
PRUint32 cxBufSize; /* sizeof buffer at cxBufPtr. */
unsigned char *cxBufPtr; /* points to real buffer, may be cxBuf. */
PRUint32 cxKeyLen; /* bytes of cxBufPtr containing key. */
PRUint32 cxDataLen; /* bytes of cxBufPtr containing data. */
SECStatus cxRv; /* records failure of void functions. */
PRBool cxIsFIPS; /* true if conforming to FIPS 198. */
HASH_HashType cxHashAlg; /* hash algorithm to use for TLS 1.2+ */
unsigned int cxOutLen; /* bytes of output if nonzero */
unsigned char cxBuf[512]; /* actual size may be larger than 512. */
} TLSPRFContext;
static void
sftk_TLSPRFHashUpdate(void *ctx, const unsigned char *data,
unsigned int data_len)
{
TLSPRFContext *cx = ctx;
PRUint32 bytesUsed = cx->cxKeyLen + cx->cxDataLen;
if (cx->cxRv != SECSuccess) /* function has previously failed. */
return;
if (bytesUsed + data_len > cx->cxBufSize) {
/* We don't use realloc here because
** (a) realloc doesn't zero out the old block, and
** (b) if realloc fails, we lose the old block.
*/
PRUint32 newBufSize = bytesUsed + data_len + 512;
unsigned char *newBuf = (unsigned char *)PORT_Alloc(newBufSize);
if (!newBuf) {
cx->cxRv = SECFailure;
return;
}
PORT_Memcpy(newBuf, cx->cxBufPtr, bytesUsed);
if (cx->cxBufPtr != cx->cxBuf) {
PORT_ZFree(cx->cxBufPtr, bytesUsed);
}
cx->cxBufPtr = newBuf;
cx->cxBufSize = newBufSize;
}
PORT_Memcpy(cx->cxBufPtr + bytesUsed, data, data_len);
cx->cxDataLen += data_len;
}
static void
sftk_TLSPRFEnd(void *ctx, unsigned char *hashout,
unsigned int *pDigestLen, unsigned int maxDigestLen)
{
*pDigestLen = 0; /* tells Verify that no data has been input yet. */
}
/* Compute the PRF values from the data previously input. */
static SECStatus
sftk_TLSPRFUpdate(void *ctx,
unsigned char *sig, /* output goes here. */
unsigned int *sigLen, /* how much output. */
unsigned int maxLen, /* output buffer size */
const unsigned char *hash, /* unused. */
unsigned int hashLen) /* unused. */
{
TLSPRFContext *cx = ctx;
SECStatus rv;
SECItem sigItem;
SECItem seedItem;
SECItem secretItem;
if (cx->cxRv != SECSuccess)
return cx->cxRv;
secretItem.data = cx->cxBufPtr;
secretItem.len = cx->cxKeyLen;
seedItem.data = cx->cxBufPtr + cx->cxKeyLen;
seedItem.len = cx->cxDataLen;
sigItem.data = sig;
if (cx->cxOutLen == 0) {
sigItem.len = maxLen;
} else if (cx->cxOutLen <= maxLen) {
sigItem.len = cx->cxOutLen;
} else {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
return SECFailure;
}
if (cx->cxHashAlg != HASH_AlgNULL) {
rv = TLS_P_hash(cx->cxHashAlg, &secretItem, NULL, &seedItem, &sigItem,
cx->cxIsFIPS);
} else {
rv = TLS_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS);
}
if (rv == SECSuccess && sigLen != NULL)
*sigLen = sigItem.len;
return rv;
}
static SECStatus
sftk_TLSPRFVerify(void *ctx,
const unsigned char *sig, /* input, for comparison. */
unsigned int sigLen, /* length of sig. */
const unsigned char *hash, /* data to be verified. */
unsigned int hashLen) /* size of hash data. */
{
TLSPRFContext *cx = ctx;
unsigned char *tmp = (unsigned char *)PORT_Alloc(sigLen);
unsigned int tmpLen = sigLen;
SECStatus rv;
if (!tmp)
return SECFailure;
if (hashLen) {
/* hashLen is non-zero when the user does a one-step verify.
** In this case, none of the data has been input yet.
*/
sftk_TLSPRFHashUpdate(cx, hash, hashLen);
}
rv = sftk_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0);
if (rv == SECSuccess) {
rv = (SECStatus)(1 - !NSS_SecureMemcmp(tmp, sig, sigLen));
}
PORT_ZFree(tmp, sigLen);
return rv;
}
static void
sftk_TLSPRFHashDestroy(void *ctx, PRBool freeit)
{
TLSPRFContext *cx = ctx;
if (freeit) {
if (cx->cxBufPtr != cx->cxBuf)
PORT_ZFree(cx->cxBufPtr, cx->cxBufSize);
PORT_ZFree(cx, cx->cxSize);
}
}
CK_RV
sftk_TLSPRFInit(SFTKSessionContext *context,
SFTKObject *key,
CK_KEY_TYPE key_type,
HASH_HashType hash_alg,
unsigned int out_len)
{
SFTKAttribute *keyVal;
TLSPRFContext *prf_cx;
CK_RV crv = CKR_HOST_MEMORY;
PRUint32 keySize;
PRUint32 blockSize;
if (key_type != CKK_GENERIC_SECRET)
return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */
context->multi = PR_TRUE;
keyVal = sftk_FindAttribute(key, CKA_VALUE);
keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen;
blockSize = keySize + sizeof(TLSPRFContext);
prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
if (!prf_cx)
goto done;
prf_cx->cxSize = blockSize;
prf_cx->cxKeyLen = keySize;
prf_cx->cxDataLen = 0;
prf_cx->cxBufSize = blockSize - offsetof(TLSPRFContext, cxBuf);
prf_cx->cxRv = SECSuccess;
prf_cx->cxIsFIPS = sftk_isFIPS(key->slot->slotID);
prf_cx->cxBufPtr = prf_cx->cxBuf;
prf_cx->cxHashAlg = hash_alg;
prf_cx->cxOutLen = out_len;
if (keySize)
PORT_Memcpy(prf_cx->cxBufPtr, keyVal->attrib.pValue, keySize);
context->hashInfo = (void *)prf_cx;
context->cipherInfo = (void *)prf_cx;
context->hashUpdate = sftk_TLSPRFHashUpdate;
context->end = sftk_TLSPRFEnd;
context->update = sftk_TLSPRFUpdate;
context->verify = sftk_TLSPRFVerify;
context->destroy = sftk_TLSPRFNull;
context->hashdestroy = sftk_TLSPRFHashDestroy;
crv = CKR_OK;
done:
if (keyVal)
sftk_FreeAttribute(keyVal);
return crv;
}