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