Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nss/lib/pk11wrap/pk11sdr.c
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "seccomon.h"
6
#include "secoid.h"
7
#include "secasn1.h"
8
#include "pkcs11.h"
9
#include "pk11func.h"
10
#include "pk11sdr.h"
11
12
/*
13
 * Data structure and template for encoding the result of an SDR operation
14
 *  This is temporary.  It should include the algorithm ID of the encryption mechanism
15
 */
16
struct SDRResult {
17
    SECItem keyid;
18
    SECAlgorithmID alg;
19
    SECItem data;
20
};
21
typedef struct SDRResult SDRResult;
22
23
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
24
25
static SEC_ASN1Template template[] = {
26
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SDRResult) },
27
    { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) },
28
    { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg),
29
      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
30
    { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) },
31
    { 0 }
32
};
33
34
static unsigned char keyID[] = {
35
    0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
37
};
38
39
static SECItem keyIDItem = {
40
    0,
41
    keyID,
42
    sizeof keyID
43
};
44
45
/* local utility function for padding an incoming data block
46
 * to the mechanism block size.
47
 */
48
static SECStatus
49
padBlock(SECItem *data, int blockSize, SECItem *result)
50
0
{
51
0
    SECStatus rv = SECSuccess;
52
0
    int padLength;
53
0
    unsigned int i;
54
55
0
    result->data = 0;
56
0
    result->len = 0;
57
58
    /* This algorithm always adds to the block (to indicate the number
59
     * of pad bytes).  So allocate a block large enough.
60
     */
61
0
    padLength = blockSize - (data->len % blockSize);
62
0
    result->len = data->len + padLength;
63
0
    result->data = (unsigned char *)PORT_Alloc(result->len);
64
65
    /* Copy the data */
66
0
    PORT_Memcpy(result->data, data->data, data->len);
67
68
    /* Add the pad values */
69
0
    for (i = data->len; i < result->len; i++)
70
0
        result->data[i] = (unsigned char)padLength;
71
72
0
    return rv;
73
0
}
74
75
static SECStatus
76
unpadBlock(SECItem *data, int blockSize, SECItem *result)
77
0
{
78
0
    SECStatus rv = SECSuccess;
79
0
    int padLength;
80
0
    unsigned int i;
81
82
0
    result->data = 0;
83
0
    result->len = 0;
84
85
    /* Remove the padding from the end if the input data */
86
0
    if (data->len == 0 || data->len % blockSize != 0) {
87
0
        rv = SECFailure;
88
0
        goto loser;
89
0
    }
90
91
0
    padLength = data->data[data->len - 1];
92
0
    if (padLength > blockSize) {
93
0
        rv = SECFailure;
94
0
        goto loser;
95
0
    }
96
97
    /* verify padding */
98
0
    for (i = data->len - padLength; i < data->len; i++) {
99
0
        if (data->data[i] != padLength) {
100
0
            rv = SECFailure;
101
0
            goto loser;
102
0
        }
103
0
    }
104
105
0
    result->len = data->len - padLength;
106
0
    result->data = (unsigned char *)PORT_Alloc(result->len);
107
0
    if (!result->data) {
108
0
        rv = SECFailure;
109
0
        goto loser;
110
0
    }
111
112
0
    PORT_Memcpy(result->data, data->data, result->len);
113
114
0
    if (padLength < 2) {
115
0
        return SECWouldBlock;
116
0
    }
117
118
0
loser:
119
0
    return rv;
120
0
}
121
122
static PRLock *pk11sdrLock = NULL;
123
124
void
125
pk11sdr_Init(void)
126
2
{
127
2
    pk11sdrLock = PR_NewLock();
128
2
}
129
130
void
131
pk11sdr_Shutdown(void)
132
0
{
133
0
    if (pk11sdrLock) {
134
0
        PR_DestroyLock(pk11sdrLock);
135
0
        pk11sdrLock = NULL;
136
0
    }
137
0
}
138
139
/*
140
 * PK11SDR_Encrypt
141
 *  Encrypt a block of data using the symmetric key identified.  The result
142
 *  is an ASN.1 (DER) encoded block of keyid, params and data.
143
 */
144
SECStatus
145
PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx)
146
0
{
147
0
    SECStatus rv = SECSuccess;
148
0
    PK11SlotInfo *slot = 0;
149
0
    PK11SymKey *key = 0;
150
0
    SECItem *params = 0;
151
0
    PK11Context *ctx = 0;
152
0
    CK_MECHANISM_TYPE type;
153
0
    SDRResult sdrResult;
154
0
    SECItem paddedData;
155
0
    SECItem *pKeyID;
156
0
    PLArenaPool *arena = 0;
157
158
    /* Initialize */
159
0
    paddedData.len = 0;
160
0
    paddedData.data = 0;
161
162
0
    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
163
0
    if (!arena) {
164
0
        rv = SECFailure;
165
0
        goto loser;
166
0
    }
167
168
    /* 1. Locate the requested keyid, or the default key (which has a keyid)
169
     * 2. Create an encryption context
170
     * 3. Encrypt
171
     * 4. Encode the results (using ASN.1)
172
     */
173
174
0
    slot = PK11_GetInternalKeySlot();
175
0
    if (!slot) {
176
0
        rv = SECFailure;
177
0
        goto loser;
178
0
    }
179
180
    /* Use triple-DES */
181
0
    type = CKM_DES3_CBC;
182
183
    /*
184
     * Login to the internal token before we look for the key, otherwise we
185
     * won't find it.
186
     */
187
0
    rv = PK11_Authenticate(slot, PR_TRUE, cx);
188
0
    if (rv != SECSuccess)
189
0
        goto loser;
190
191
    /* Find the key to use */
192
0
    pKeyID = keyid;
193
0
    if (pKeyID->len == 0) {
194
0
        pKeyID = &keyIDItem; /* Use default value */
195
196
        /* put in a course lock to prevent a race between not finding the
197
         * key and creating  one.
198
         */
199
200
0
        if (pk11sdrLock)
201
0
            PR_Lock(pk11sdrLock);
202
203
        /* Try to find the key */
204
0
        key = PK11_FindFixedKey(slot, type, pKeyID, cx);
205
206
        /* If the default key doesn't exist yet, try to create it */
207
0
        if (!key)
208
0
            key = PK11_GenDES3TokenKey(slot, pKeyID, cx);
209
0
        if (pk11sdrLock)
210
0
            PR_Unlock(pk11sdrLock);
211
0
    } else {
212
0
        key = PK11_FindFixedKey(slot, type, pKeyID, cx);
213
0
    }
214
215
0
    if (!key) {
216
0
        rv = SECFailure;
217
0
        goto loser;
218
0
    }
219
220
0
    params = PK11_GenerateNewParam(type, key);
221
0
    if (!params) {
222
0
        rv = SECFailure;
223
0
        goto loser;
224
0
    }
225
226
0
    ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params);
227
0
    if (!ctx) {
228
0
        rv = SECFailure;
229
0
        goto loser;
230
0
    }
231
232
0
    rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData);
233
0
    if (rv != SECSuccess)
234
0
        goto loser;
235
236
0
    sdrResult.data.len = paddedData.len;
237
0
    sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len);
238
239
0
    rv = PK11_CipherOp(ctx, sdrResult.data.data, (int *)&sdrResult.data.len, sdrResult.data.len,
240
0
                       paddedData.data, paddedData.len);
241
0
    if (rv != SECSuccess)
242
0
        goto loser;
243
244
0
    PK11_Finalize(ctx);
245
246
0
    sdrResult.keyid = *pKeyID;
247
248
0
    rv = PK11_ParamToAlgid(SEC_OID_DES_EDE3_CBC, params, arena, &sdrResult.alg);
249
0
    if (rv != SECSuccess)
250
0
        goto loser;
251
252
0
    if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) {
253
0
        rv = SECFailure;
254
0
        goto loser;
255
0
    }
256
257
0
loser:
258
0
    SECITEM_ZfreeItem(&paddedData, PR_FALSE);
259
0
    if (arena)
260
0
        PORT_FreeArena(arena, PR_TRUE);
261
0
    if (ctx)
262
0
        PK11_DestroyContext(ctx, PR_TRUE);
263
0
    if (params)
264
0
        SECITEM_ZfreeItem(params, PR_TRUE);
265
0
    if (key)
266
0
        PK11_FreeSymKey(key);
267
0
    if (slot)
268
0
        PK11_FreeSlot(slot);
269
270
0
    return rv;
271
0
}
272
273
/* decrypt a block */
274
static SECStatus
275
pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena,
276
            CK_MECHANISM_TYPE type, PK11SymKey *key,
277
            SECItem *params, SECItem *in, SECItem *result)
278
0
{
279
0
    PK11Context *ctx = 0;
280
0
    SECItem paddedResult;
281
0
    SECStatus rv;
282
283
0
    paddedResult.len = 0;
284
0
    paddedResult.data = 0;
285
286
0
    ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params);
287
0
    if (!ctx) {
288
0
        rv = SECFailure;
289
0
        goto loser;
290
0
    }
291
292
0
    paddedResult.len = in->len;
293
0
    paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len);
294
295
0
    rv = PK11_CipherOp(ctx, paddedResult.data,
296
0
                       (int *)&paddedResult.len, paddedResult.len,
297
0
                       in->data, in->len);
298
0
    if (rv != SECSuccess)
299
0
        goto loser;
300
301
0
    PK11_Finalize(ctx);
302
303
    /* Remove the padding */
304
0
    rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result);
305
0
    if (rv)
306
0
        goto loser;
307
308
0
loser:
309
0
    if (ctx)
310
0
        PK11_DestroyContext(ctx, PR_TRUE);
311
0
    return rv;
312
0
}
313
314
/*
315
 * PK11SDR_Decrypt
316
 *  Decrypt a block of data produced by PK11SDR_Encrypt.  The key used is identified
317
 *  by the keyid field within the input.
318
 */
319
SECStatus
320
PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx)
321
0
{
322
0
    SECStatus rv = SECSuccess;
323
0
    PK11SlotInfo *slot = 0;
324
0
    PK11SymKey *key = 0;
325
0
    CK_MECHANISM_TYPE type;
326
0
    SDRResult sdrResult;
327
0
    SECItem *params = 0;
328
0
    SECItem possibleResult = { 0, NULL, 0 };
329
0
    PLArenaPool *arena = 0;
330
331
0
    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
332
0
    if (!arena) {
333
0
        rv = SECFailure;
334
0
        goto loser;
335
0
    }
336
337
    /* Decode the incoming data */
338
0
    memset(&sdrResult, 0, sizeof sdrResult);
339
0
    rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data);
340
0
    if (rv != SECSuccess)
341
0
        goto loser; /* Invalid format */
342
343
    /* Find the slot and key for the given keyid */
344
0
    slot = PK11_GetInternalKeySlot();
345
0
    if (!slot) {
346
0
        rv = SECFailure;
347
0
        goto loser;
348
0
    }
349
350
0
    rv = PK11_Authenticate(slot, PR_TRUE, cx);
351
0
    if (rv != SECSuccess)
352
0
        goto loser;
353
354
    /* Get the parameter values from the data */
355
0
    params = PK11_ParamFromAlgid(&sdrResult.alg);
356
0
    if (!params) {
357
0
        rv = SECFailure;
358
0
        goto loser;
359
0
    }
360
361
    /* Use triple-DES (Should look up the algorithm) */
362
0
    type = CKM_DES3_CBC;
363
0
    key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx);
364
0
    if (!key) {
365
0
        rv = SECFailure;
366
0
    } else {
367
0
        rv = pk11Decrypt(slot, arena, type, key, params,
368
0
                         &sdrResult.data, result);
369
0
    }
370
371
    /*
372
     * if the pad value was too small (1 or 2), then it's statistically
373
     * 'likely' that (1 in 256) that we may not have the correct key.
374
     * Check the other keys for a better match. If we find none, use
375
     * this result.
376
     */
377
0
    if (rv == SECWouldBlock) {
378
0
        possibleResult = *result;
379
0
    }
380
381
    /*
382
     * handle the case where your key indicies may have been broken
383
     */
384
0
    if (rv != SECSuccess) {
385
0
        PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx);
386
0
        PK11SymKey *testKey = NULL;
387
0
        PK11SymKey *nextKey = NULL;
388
389
0
        for (testKey = keyList; testKey;
390
0
             testKey = PK11_GetNextSymKey(testKey)) {
391
0
            rv = pk11Decrypt(slot, arena, type, testKey, params,
392
0
                             &sdrResult.data, result);
393
0
            if (rv == SECSuccess) {
394
0
                break;
395
0
            }
396
            /* found a close match. If it's our first remember it */
397
0
            if (rv == SECWouldBlock) {
398
0
                if (possibleResult.data) {
399
                    /* this is unlikely but possible. If we hit this condition,
400
                     * we have no way of knowing which possibility to prefer.
401
                     * in this case we just match the key the application
402
                     * thought was the right one */
403
0
                    SECITEM_ZfreeItem(result, PR_FALSE);
404
0
                } else {
405
0
                    possibleResult = *result;
406
0
                }
407
0
            }
408
0
        }
409
410
        /* free the list */
411
0
        for (testKey = keyList; testKey; testKey = nextKey) {
412
0
            nextKey = PK11_GetNextSymKey(testKey);
413
0
            PK11_FreeSymKey(testKey);
414
0
        }
415
0
    }
416
417
    /* we didn't find a better key, use the one with a small pad value */
418
0
    if ((rv != SECSuccess) && (possibleResult.data)) {
419
0
        *result = possibleResult;
420
0
        possibleResult.data = NULL;
421
0
        rv = SECSuccess;
422
0
    }
423
424
0
loser:
425
0
    if (arena)
426
0
        PORT_FreeArena(arena, PR_TRUE);
427
0
    if (key)
428
0
        PK11_FreeSymKey(key);
429
0
    if (params)
430
0
        SECITEM_ZfreeItem(params, PR_TRUE);
431
0
    if (slot)
432
0
        PK11_FreeSlot(slot);
433
0
    if (possibleResult.data)
434
0
        SECITEM_ZfreeItem(&possibleResult, PR_FALSE);
435
436
0
    return rv;
437
0
}