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
sftk_TLSPRFNull(void * data,PRBool freeit)12 sftk_TLSPRFNull(void *data, PRBool freeit)
13 {
14 return;
15 }
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
sftk_TLSPRFHashUpdate(TLSPRFContext * cx,const unsigned char * data,unsigned int data_len)31 sftk_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data,
32 unsigned int data_len)
33 {
34 PRUint32 bytesUsed = cx->cxKeyLen + cx->cxDataLen;
35
36 if (cx->cxRv != SECSuccess) /* function has previously failed. */
37 return;
38 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 PRUint32 newBufSize = bytesUsed + data_len + 512;
44 unsigned char *newBuf = (unsigned char *)PORT_Alloc(newBufSize);
45 if (!newBuf) {
46 cx->cxRv = SECFailure;
47 return;
48 }
49 PORT_Memcpy(newBuf, cx->cxBufPtr, bytesUsed);
50 if (cx->cxBufPtr != cx->cxBuf) {
51 PORT_ZFree(cx->cxBufPtr, bytesUsed);
52 }
53 cx->cxBufPtr = newBuf;
54 cx->cxBufSize = newBufSize;
55 }
56 PORT_Memcpy(cx->cxBufPtr + bytesUsed, data, data_len);
57 cx->cxDataLen += data_len;
58 }
59
60 static void
sftk_TLSPRFEnd(TLSPRFContext * ctx,unsigned char * hashout,unsigned int * pDigestLen,unsigned int maxDigestLen)61 sftk_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout,
62 unsigned int *pDigestLen, unsigned int maxDigestLen)
63 {
64 *pDigestLen = 0; /* tells Verify that no data has been input yet. */
65 }
66
67 /* Compute the PRF values from the data previously input. */
68 static SECStatus
sftk_TLSPRFUpdate(TLSPRFContext * cx,unsigned char * sig,unsigned int * sigLen,unsigned int maxLen,unsigned char * hash,unsigned int hashLen)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 {
76 SECStatus rv;
77 SECItem sigItem;
78 SECItem seedItem;
79 SECItem secretItem;
80
81 if (cx->cxRv != SECSuccess)
82 return cx->cxRv;
83
84 secretItem.data = cx->cxBufPtr;
85 secretItem.len = cx->cxKeyLen;
86
87 seedItem.data = cx->cxBufPtr + cx->cxKeyLen;
88 seedItem.len = cx->cxDataLen;
89
90 sigItem.data = sig;
91 if (cx->cxOutLen == 0) {
92 sigItem.len = maxLen;
93 } else if (cx->cxOutLen <= maxLen) {
94 sigItem.len = cx->cxOutLen;
95 } else {
96 PORT_SetError(SEC_ERROR_OUTPUT_LEN);
97 return SECFailure;
98 }
99
100 if (cx->cxHashAlg != HASH_AlgNULL) {
101 rv = TLS_P_hash(cx->cxHashAlg, &secretItem, NULL, &seedItem, &sigItem,
102 cx->cxIsFIPS);
103 } else {
104 rv = TLS_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS);
105 }
106 if (rv == SECSuccess && sigLen != NULL)
107 *sigLen = sigItem.len;
108 return rv;
109 }
110
111 static SECStatus
sftk_TLSPRFVerify(TLSPRFContext * cx,unsigned char * sig,unsigned int sigLen,unsigned char * hash,unsigned int hashLen)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 {
118 unsigned char *tmp = (unsigned char *)PORT_Alloc(sigLen);
119 unsigned int tmpLen = sigLen;
120 SECStatus rv;
121
122 if (!tmp)
123 return SECFailure;
124 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 sftk_TLSPRFHashUpdate(cx, hash, hashLen);
129 }
130 rv = sftk_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0);
131 if (rv == SECSuccess) {
132 rv = (SECStatus)(1 - !NSS_SecureMemcmp(tmp, sig, sigLen));
133 }
134 PORT_ZFree(tmp, sigLen);
135 return rv;
136 }
137
138 static void
sftk_TLSPRFHashDestroy(TLSPRFContext * cx,PRBool freeit)139 sftk_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit)
140 {
141 if (freeit) {
142 if (cx->cxBufPtr != cx->cxBuf)
143 PORT_ZFree(cx->cxBufPtr, cx->cxBufSize);
144 PORT_ZFree(cx, cx->cxSize);
145 }
146 }
147
148 CK_RV
sftk_TLSPRFInit(SFTKSessionContext * context,SFTKObject * key,CK_KEY_TYPE key_type,HASH_HashType hash_alg,unsigned int out_len)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 {
155 SFTKAttribute *keyVal;
156 TLSPRFContext *prf_cx;
157 CK_RV crv = CKR_HOST_MEMORY;
158 PRUint32 keySize;
159 PRUint32 blockSize;
160
161 if (key_type != CKK_GENERIC_SECRET)
162 return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */
163
164 context->multi = PR_TRUE;
165
166 keyVal = sftk_FindAttribute(key, CKA_VALUE);
167 keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen;
168 blockSize = keySize + sizeof(TLSPRFContext);
169 prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
170 if (!prf_cx)
171 goto done;
172 prf_cx->cxSize = blockSize;
173 prf_cx->cxKeyLen = keySize;
174 prf_cx->cxDataLen = 0;
175 prf_cx->cxBufSize = blockSize - offsetof(TLSPRFContext, cxBuf);
176 prf_cx->cxRv = SECSuccess;
177 prf_cx->cxIsFIPS = sftk_isFIPS(key->slot->slotID);
178 prf_cx->cxBufPtr = prf_cx->cxBuf;
179 prf_cx->cxHashAlg = hash_alg;
180 prf_cx->cxOutLen = out_len;
181 if (keySize)
182 PORT_Memcpy(prf_cx->cxBufPtr, keyVal->attrib.pValue, keySize);
183
184 context->hashInfo = (void *)prf_cx;
185 context->cipherInfo = (void *)prf_cx;
186 context->hashUpdate = (SFTKHash)sftk_TLSPRFHashUpdate;
187 context->end = (SFTKEnd)sftk_TLSPRFEnd;
188 context->update = (SFTKCipher)sftk_TLSPRFUpdate;
189 context->verify = (SFTKVerify)sftk_TLSPRFVerify;
190 context->destroy = (SFTKDestroy)sftk_TLSPRFNull;
191 context->hashdestroy = (SFTKDestroy)sftk_TLSPRFHashDestroy;
192 crv = CKR_OK;
193
194 done:
195 if (keyVal)
196 sftk_FreeAttribute(keyVal);
197 return crv;
198 }
199