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