summaryrefslogtreecommitdiff
path: root/lib/softoken/tlsprf.c
blob: 042cf194f7ac86d5b618e714d06002aa182c8996 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* tlsprf.c - TLS Pseudo Random Function (PRF) implementation
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "pkcs11i.h"
#include "blapi.h"
#include "secerr.h"

static void
sftk_TLSPRFNull(void *data, PRBool freeit)
{
    return;
}

typedef struct {
    PRUint32 cxSize;          /* size of allocated block, in bytes.        */
    PRUint32 cxBufSize;       /* sizeof buffer at cxBufPtr.                */
    unsigned char *cxBufPtr;  /* points to real buffer, may be cxBuf.      */
    PRUint32 cxKeyLen;        /* bytes of cxBufPtr containing key.         */
    PRUint32 cxDataLen;       /* bytes of cxBufPtr containing data.        */
    SECStatus cxRv;           /* records failure of void functions.        */
    PRBool cxIsFIPS;          /* true if conforming to FIPS 198.           */
    HASH_HashType cxHashAlg;  /* hash algorithm to use for TLS 1.2+        */
    unsigned int cxOutLen;    /* bytes of output if nonzero                */
    unsigned char cxBuf[512]; /* actual size may be larger than 512.       */
} TLSPRFContext;

static void
sftk_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data,
                      unsigned int data_len)
{
    PRUint32 bytesUsed = cx->cxKeyLen + cx->cxDataLen;

    if (cx->cxRv != SECSuccess) /* function has previously failed. */
        return;
    if (bytesUsed + data_len > cx->cxBufSize) {
        /* We don't use realloc here because
        ** (a) realloc doesn't zero out the old block, and
        ** (b) if realloc fails, we lose the old block.
        */
        PRUint32 newBufSize = bytesUsed + data_len + 512;
        unsigned char *newBuf = (unsigned char *)PORT_Alloc(newBufSize);
        if (!newBuf) {
            cx->cxRv = SECFailure;
            return;
        }
        PORT_Memcpy(newBuf, cx->cxBufPtr, bytesUsed);
        if (cx->cxBufPtr != cx->cxBuf) {
            PORT_ZFree(cx->cxBufPtr, bytesUsed);
        }
        cx->cxBufPtr = newBuf;
        cx->cxBufSize = newBufSize;
    }
    PORT_Memcpy(cx->cxBufPtr + bytesUsed, data, data_len);
    cx->cxDataLen += data_len;
}

static void
sftk_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout,
               unsigned int *pDigestLen, unsigned int maxDigestLen)
{
    *pDigestLen = 0; /* tells Verify that no data has been input yet. */
}

/* Compute the PRF values from the data previously input. */
static SECStatus
sftk_TLSPRFUpdate(TLSPRFContext *cx,
                  unsigned char *sig,   /* output goes here. */
                  unsigned int *sigLen, /* how much output.  */
                  unsigned int maxLen,  /* output buffer size */
                  unsigned char *hash,  /* unused. */
                  unsigned int hashLen) /* unused. */
{
    SECStatus rv;
    SECItem sigItem;
    SECItem seedItem;
    SECItem secretItem;

    if (cx->cxRv != SECSuccess)
        return cx->cxRv;

    secretItem.data = cx->cxBufPtr;
    secretItem.len = cx->cxKeyLen;

    seedItem.data = cx->cxBufPtr + cx->cxKeyLen;
    seedItem.len = cx->cxDataLen;

    sigItem.data = sig;
    if (cx->cxOutLen == 0) {
        sigItem.len = maxLen;
    } else if (cx->cxOutLen <= maxLen) {
        sigItem.len = cx->cxOutLen;
    } else {
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
        return SECFailure;
    }

    if (cx->cxHashAlg != HASH_AlgNULL) {
        rv = TLS_P_hash(cx->cxHashAlg, &secretItem, NULL, &seedItem, &sigItem,
                        cx->cxIsFIPS);
    } else {
        rv = TLS_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS);
    }
    if (rv == SECSuccess && sigLen != NULL)
        *sigLen = sigItem.len;
    return rv;
}

static SECStatus
sftk_TLSPRFVerify(TLSPRFContext *cx,
                  unsigned char *sig,   /* input, for comparison. */
                  unsigned int sigLen,  /* length of sig.         */
                  unsigned char *hash,  /* data to be verified.   */
                  unsigned int hashLen) /* size of hash data.     */
{
    unsigned char *tmp = (unsigned char *)PORT_Alloc(sigLen);
    unsigned int tmpLen = sigLen;
    SECStatus rv;

    if (!tmp)
        return SECFailure;
    if (hashLen) {
        /* hashLen is non-zero when the user does a one-step verify.
        ** In this case, none of the data has been input yet.
        */
        sftk_TLSPRFHashUpdate(cx, hash, hashLen);
    }
    rv = sftk_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0);
    if (rv == SECSuccess) {
        rv = (SECStatus)(1 - !NSS_SecureMemcmp(tmp, sig, sigLen));
    }
    PORT_ZFree(tmp, sigLen);
    return rv;
}

static void
sftk_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit)
{
    if (freeit) {
        if (cx->cxBufPtr != cx->cxBuf)
            PORT_ZFree(cx->cxBufPtr, cx->cxBufSize);
        PORT_ZFree(cx, cx->cxSize);
    }
}

CK_RV
sftk_TLSPRFInit(SFTKSessionContext *context,
                SFTKObject *key,
                CK_KEY_TYPE key_type,
                HASH_HashType hash_alg,
                unsigned int out_len)
{
    SFTKAttribute *keyVal;
    TLSPRFContext *prf_cx;
    CK_RV crv = CKR_HOST_MEMORY;
    PRUint32 keySize;
    PRUint32 blockSize;

    if (key_type != CKK_GENERIC_SECRET)
        return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */

    context->multi = PR_TRUE;

    keyVal = sftk_FindAttribute(key, CKA_VALUE);
    keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen;
    blockSize = keySize + sizeof(TLSPRFContext);
    prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
    if (!prf_cx)
        goto done;
    prf_cx->cxSize = blockSize;
    prf_cx->cxKeyLen = keySize;
    prf_cx->cxDataLen = 0;
    prf_cx->cxBufSize = blockSize - offsetof(TLSPRFContext, cxBuf);
    prf_cx->cxRv = SECSuccess;
    prf_cx->cxIsFIPS = sftk_isFIPS(key->slot->slotID);
    prf_cx->cxBufPtr = prf_cx->cxBuf;
    prf_cx->cxHashAlg = hash_alg;
    prf_cx->cxOutLen = out_len;
    if (keySize)
        PORT_Memcpy(prf_cx->cxBufPtr, keyVal->attrib.pValue, keySize);

    context->hashInfo = (void *)prf_cx;
    context->cipherInfo = (void *)prf_cx;
    context->hashUpdate = (SFTKHash)sftk_TLSPRFHashUpdate;
    context->end = (SFTKEnd)sftk_TLSPRFEnd;
    context->update = (SFTKCipher)sftk_TLSPRFUpdate;
    context->verify = (SFTKVerify)sftk_TLSPRFVerify;
    context->destroy = (SFTKDestroy)sftk_TLSPRFNull;
    context->hashdestroy = (SFTKDestroy)sftk_TLSPRFHashDestroy;
    crv = CKR_OK;

done:
    if (keyVal)
        sftk_FreeAttribute(keyVal);
    return crv;
}