/* 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(TLSPRFContext *cx, const unsigned char *data, unsigned int data_len) { 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(TLSPRFContext *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(TLSPRFContext *cx, unsigned char *sig, /* output goes here. */ unsigned int *sigLen, /* how much output. */ unsigned int maxLen, /* output buffer size */ unsigned char *hash, /* unused. */ unsigned int hashLen) /* unused. */ { 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(TLSPRFContext *cx, unsigned char *sig, /* input, for comparison. */ unsigned int sigLen, /* length of sig. */ unsigned char *hash, /* data to be verified. */ unsigned int hashLen) /* size of hash data. */ { 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(TLSPRFContext *cx, PRBool freeit) { 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 = (SFTKHash)sftk_TLSPRFHashUpdate; context->end = (SFTKEnd)sftk_TLSPRFEnd; context->update = (SFTKCipher)sftk_TLSPRFUpdate; context->verify = (SFTKVerify)sftk_TLSPRFVerify; context->destroy = (SFTKDestroy)sftk_TLSPRFNull; context->hashdestroy = (SFTKDestroy)sftk_TLSPRFHashDestroy; crv = CKR_OK; done: if (keyVal) sftk_FreeAttribute(keyVal); return crv; }