/src/nss/lib/softoken/tlsprf.c
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | /* tlsprf.c - TLS Pseudo Random Function (PRF) implementation | 
| 2 |  |  * | 
| 3 |  |  * This Source Code Form is subject to the terms of the Mozilla Public | 
| 4 |  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | 
| 5 |  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
| 6 |  |  | 
| 7 |  | #include "pkcs11i.h" | 
| 8 |  | #include "blapi.h" | 
| 9 |  | #include "secerr.h" | 
| 10 |  |  | 
| 11 |  | static void | 
| 12 |  | sftk_TLSPRFNull(void *data, PRBool freeit) | 
| 13 | 30 | { | 
| 14 | 30 |     return; | 
| 15 | 30 | } | 
| 16 |  |  | 
| 17 |  | typedef struct { | 
| 18 |  |     PRUint32 cxSize;          /* size of allocated block, in bytes.        */ | 
| 19 |  |     PRUint32 cxBufSize;       /* sizeof buffer at cxBufPtr.                */ | 
| 20 |  |     unsigned char *cxBufPtr;  /* points to real buffer, may be cxBuf.      */ | 
| 21 |  |     PRUint32 cxKeyLen;        /* bytes of cxBufPtr containing key.         */ | 
| 22 |  |     PRUint32 cxDataLen;       /* bytes of cxBufPtr containing data.        */ | 
| 23 |  |     SECStatus cxRv;           /* records failure of void functions.        */ | 
| 24 |  |     PRBool cxIsFIPS;          /* true if conforming to FIPS 198.           */ | 
| 25 |  |     HASH_HashType cxHashAlg;  /* hash algorithm to use for TLS 1.2+        */ | 
| 26 |  |     unsigned int cxOutLen;    /* bytes of output if nonzero                */ | 
| 27 |  |     unsigned char cxBuf[512]; /* actual size may be larger than 512.       */ | 
| 28 |  | } TLSPRFContext; | 
| 29 |  |  | 
| 30 |  | static void | 
| 31 |  | sftk_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data, | 
| 32 |  |                       unsigned int data_len) | 
| 33 | 60 | { | 
| 34 | 60 |     PRUint32 bytesUsed = cx->cxKeyLen + cx->cxDataLen; | 
| 35 |  |  | 
| 36 | 60 |     if (cx->cxRv != SECSuccess) /* function has previously failed. */ | 
| 37 | 0 |         return; | 
| 38 | 60 |     if (bytesUsed + data_len > cx->cxBufSize) { | 
| 39 |  |         /* We don't use realloc here because | 
| 40 |  |         ** (a) realloc doesn't zero out the old block, and | 
| 41 |  |         ** (b) if realloc fails, we lose the old block. | 
| 42 |  |         */ | 
| 43 | 0 |         PRUint32 newBufSize = bytesUsed + data_len + 512; | 
| 44 | 0 |         unsigned char *newBuf = (unsigned char *)PORT_Alloc(newBufSize); | 
| 45 | 0 |         if (!newBuf) { | 
| 46 | 0 |             cx->cxRv = SECFailure; | 
| 47 | 0 |             return; | 
| 48 | 0 |         } | 
| 49 | 0 |         PORT_Memcpy(newBuf, cx->cxBufPtr, bytesUsed); | 
| 50 | 0 |         if (cx->cxBufPtr != cx->cxBuf) { | 
| 51 | 0 |             PORT_ZFree(cx->cxBufPtr, bytesUsed); | 
| 52 | 0 |         } | 
| 53 | 0 |         cx->cxBufPtr = newBuf; | 
| 54 | 0 |         cx->cxBufSize = newBufSize; | 
| 55 | 0 |     } | 
| 56 | 60 |     PORT_Memcpy(cx->cxBufPtr + bytesUsed, data, data_len); | 
| 57 | 60 |     cx->cxDataLen += data_len; | 
| 58 | 60 | } | 
| 59 |  |  | 
| 60 |  | static void | 
| 61 |  | sftk_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout, | 
| 62 |  |                unsigned int *pDigestLen, unsigned int maxDigestLen) | 
| 63 | 30 | { | 
| 64 | 30 |     *pDigestLen = 0; /* tells Verify that no data has been input yet. */ | 
| 65 | 30 | } | 
| 66 |  |  | 
| 67 |  | /* Compute the PRF values from the data previously input. */ | 
| 68 |  | static SECStatus | 
| 69 |  | sftk_TLSPRFUpdate(TLSPRFContext *cx, | 
| 70 |  |                   unsigned char *sig,   /* output goes here. */ | 
| 71 |  |                   unsigned int *sigLen, /* how much output.  */ | 
| 72 |  |                   unsigned int maxLen,  /* output buffer size */ | 
| 73 |  |                   unsigned char *hash,  /* unused. */ | 
| 74 |  |                   unsigned int hashLen) /* unused. */ | 
| 75 | 30 | { | 
| 76 | 30 |     SECStatus rv; | 
| 77 | 30 |     SECItem sigItem; | 
| 78 | 30 |     SECItem seedItem; | 
| 79 | 30 |     SECItem secretItem; | 
| 80 |  |  | 
| 81 | 30 |     if (cx->cxRv != SECSuccess) | 
| 82 | 0 |         return cx->cxRv; | 
| 83 |  |  | 
| 84 | 30 |     secretItem.data = cx->cxBufPtr; | 
| 85 | 30 |     secretItem.len = cx->cxKeyLen; | 
| 86 |  |  | 
| 87 | 30 |     seedItem.data = cx->cxBufPtr + cx->cxKeyLen; | 
| 88 | 30 |     seedItem.len = cx->cxDataLen; | 
| 89 |  |  | 
| 90 | 30 |     sigItem.data = sig; | 
| 91 | 30 |     if (cx->cxOutLen == 0) { | 
| 92 | 0 |         sigItem.len = maxLen; | 
| 93 | 30 |     } else if (cx->cxOutLen <= maxLen) { | 
| 94 | 30 |         sigItem.len = cx->cxOutLen; | 
| 95 | 30 |     } else { | 
| 96 | 0 |         PORT_SetError(SEC_ERROR_OUTPUT_LEN); | 
| 97 | 0 |         return SECFailure; | 
| 98 | 0 |     } | 
| 99 |  |  | 
| 100 | 30 |     if (cx->cxHashAlg != HASH_AlgNULL) { | 
| 101 | 30 |         rv = TLS_P_hash(cx->cxHashAlg, &secretItem, NULL, &seedItem, &sigItem, | 
| 102 | 30 |                         cx->cxIsFIPS); | 
| 103 | 30 |     } else { | 
| 104 | 0 |         rv = TLS_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS); | 
| 105 | 0 |     } | 
| 106 | 30 |     if (rv == SECSuccess && sigLen != NULL) | 
| 107 | 30 |         *sigLen = sigItem.len; | 
| 108 | 30 |     return rv; | 
| 109 | 30 | } | 
| 110 |  |  | 
| 111 |  | static SECStatus | 
| 112 |  | sftk_TLSPRFVerify(TLSPRFContext *cx, | 
| 113 |  |                   unsigned char *sig,   /* input, for comparison. */ | 
| 114 |  |                   unsigned int sigLen,  /* length of sig.         */ | 
| 115 |  |                   unsigned char *hash,  /* data to be verified.   */ | 
| 116 |  |                   unsigned int hashLen) /* size of hash data.     */ | 
| 117 | 0 | { | 
| 118 | 0 |     unsigned char *tmp = (unsigned char *)PORT_Alloc(sigLen); | 
| 119 | 0 |     unsigned int tmpLen = sigLen; | 
| 120 | 0 |     SECStatus rv; | 
| 121 |  | 
 | 
| 122 | 0 |     if (!tmp) | 
| 123 | 0 |         return SECFailure; | 
| 124 | 0 |     if (hashLen) { | 
| 125 |  |         /* hashLen is non-zero when the user does a one-step verify. | 
| 126 |  |         ** In this case, none of the data has been input yet. | 
| 127 |  |         */ | 
| 128 | 0 |         sftk_TLSPRFHashUpdate(cx, hash, hashLen); | 
| 129 | 0 |     } | 
| 130 | 0 |     rv = sftk_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0); | 
| 131 | 0 |     if (rv == SECSuccess) { | 
| 132 | 0 |         rv = (SECStatus)(1 - !NSS_SecureMemcmp(tmp, sig, sigLen)); | 
| 133 | 0 |     } | 
| 134 | 0 |     PORT_ZFree(tmp, sigLen); | 
| 135 | 0 |     return rv; | 
| 136 | 0 | } | 
| 137 |  |  | 
| 138 |  | static void | 
| 139 |  | sftk_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit) | 
| 140 | 30 | { | 
| 141 | 30 |     if (freeit) { | 
| 142 | 30 |         if (cx->cxBufPtr != cx->cxBuf) | 
| 143 | 0 |             PORT_ZFree(cx->cxBufPtr, cx->cxBufSize); | 
| 144 | 30 |         PORT_ZFree(cx, cx->cxSize); | 
| 145 | 30 |     } | 
| 146 | 30 | } | 
| 147 |  |  | 
| 148 |  | CK_RV | 
| 149 |  | sftk_TLSPRFInit(SFTKSessionContext *context, | 
| 150 |  |                 SFTKObject *key, | 
| 151 |  |                 CK_KEY_TYPE key_type, | 
| 152 |  |                 HASH_HashType hash_alg, | 
| 153 |  |                 unsigned int out_len) | 
| 154 | 30 | { | 
| 155 | 30 |     SFTKAttribute *keyVal; | 
| 156 | 30 |     TLSPRFContext *prf_cx; | 
| 157 | 30 |     CK_RV crv = CKR_HOST_MEMORY; | 
| 158 | 30 |     PRUint32 keySize; | 
| 159 | 30 |     PRUint32 blockSize; | 
| 160 |  |  | 
| 161 | 30 |     if (key_type != CKK_GENERIC_SECRET) | 
| 162 | 0 |         return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */ | 
| 163 |  |  | 
| 164 | 30 |     context->multi = PR_TRUE; | 
| 165 |  |  | 
| 166 | 30 |     keyVal = sftk_FindAttribute(key, CKA_VALUE); | 
| 167 | 30 |     keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen; | 
| 168 | 30 |     blockSize = keySize + sizeof(TLSPRFContext); | 
| 169 | 30 |     prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize); | 
| 170 | 30 |     if (!prf_cx) | 
| 171 | 0 |         goto done; | 
| 172 | 30 |     prf_cx->cxSize = blockSize; | 
| 173 | 30 |     prf_cx->cxKeyLen = keySize; | 
| 174 | 30 |     prf_cx->cxDataLen = 0; | 
| 175 | 30 |     prf_cx->cxBufSize = blockSize - offsetof(TLSPRFContext, cxBuf); | 
| 176 | 30 |     prf_cx->cxRv = SECSuccess; | 
| 177 | 30 |     prf_cx->cxIsFIPS = sftk_isFIPS(key->slot->slotID); | 
| 178 | 30 |     prf_cx->cxBufPtr = prf_cx->cxBuf; | 
| 179 | 30 |     prf_cx->cxHashAlg = hash_alg; | 
| 180 | 30 |     prf_cx->cxOutLen = out_len; | 
| 181 | 30 |     if (keySize) | 
| 182 | 30 |         PORT_Memcpy(prf_cx->cxBufPtr, keyVal->attrib.pValue, keySize); | 
| 183 |  |  | 
| 184 | 30 |     context->hashInfo = (void *)prf_cx; | 
| 185 | 30 |     context->cipherInfo = (void *)prf_cx; | 
| 186 | 30 |     context->hashUpdate = (SFTKHash)sftk_TLSPRFHashUpdate; | 
| 187 | 30 |     context->end = (SFTKEnd)sftk_TLSPRFEnd; | 
| 188 | 30 |     context->update = (SFTKCipher)sftk_TLSPRFUpdate; | 
| 189 | 30 |     context->verify = (SFTKVerify)sftk_TLSPRFVerify; | 
| 190 | 30 |     context->destroy = (SFTKDestroy)sftk_TLSPRFNull; | 
| 191 | 30 |     context->hashdestroy = (SFTKDestroy)sftk_TLSPRFHashDestroy; | 
| 192 | 30 |     crv = CKR_OK; | 
| 193 |  |  | 
| 194 | 30 | done: | 
| 195 | 30 |     if (keyVal) | 
| 196 | 30 |         sftk_FreeAttribute(keyVal); | 
| 197 | 30 |     return crv; | 
| 198 | 30 | } |