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