Coverage Report

Created: 2025-07-01 06:26

/src/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
4
{
127
4
    pk11sdrLock = PR_NewLock();
128
4
}
129
130
void
131
pk11sdr_Shutdown(void)
132
4
{
133
4
    if (pk11sdrLock) {
134
4
        PR_DestroyLock(pk11sdrLock);
135
4
        pk11sdrLock = NULL;
136
4
    }
137
4
}
138
139
/*
140
 * PK11SDR_Encrypt
141
 *  Deprecated version of PK11SDR_EncryptWithMechanism using DES3_CBC.
142
 */
143
SECStatus
144
PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx)
145
0
{
146
0
    return PK11SDR_EncryptWithMechanism(NULL, keyid, CKM_DES3_CBC, data, result, cx);
147
0
}
148
149
/*
150
 * PK11SDR_EncryptWithMechanism
151
 *  Encrypt a block of data using the symmetric key identified and the
152
 *  encryption mechanism specified (only AES_CBC and DES3_CBC are supported).
153
 *  The result is an ASN.1 (DER) encoded block of keyid, params and data.
154
 */
155
SECStatus
156
PK11SDR_EncryptWithMechanism(PK11SlotInfo *slot, SECItem *keyid, CK_MECHANISM_TYPE type, SECItem *data, SECItem *result, void *cx)
157
0
{
158
0
    SECStatus rv = SECSuccess;
159
0
    PK11SymKey *key = 0;
160
0
    SECItem *params = 0;
161
0
    PK11Context *ctx = 0;
162
0
    SDRResult sdrResult;
163
0
    SECItem paddedData;
164
0
    SECItem *pKeyID;
165
0
    PLArenaPool *arena = 0;
166
0
    SECOidTag algtag;
167
0
    PK11SlotInfo *aSlot = slot;
168
169
    /* Initialize */
170
0
    paddedData.len = 0;
171
0
    paddedData.data = 0;
172
173
0
    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
174
0
    if (!arena) {
175
0
        rv = SECFailure;
176
0
        goto loser;
177
0
    }
178
179
    /* 1. Locate the requested keyid, or the default key (which has a keyid)
180
     * 2. Create an encryption context
181
     * 3. Encrypt
182
     * 4. Encode the results (using ASN.1)
183
     */
184
185
0
    if (!slot) {
186
0
        slot = PK11_GetInternalKeySlot();
187
0
        if (!slot) {
188
0
            rv = SECFailure;
189
0
            goto loser;
190
0
        }
191
0
    }
192
193
    /*
194
     * Login to the internal token before we look for the key, otherwise we
195
     * won't find it.
196
     */
197
0
    rv = PK11_Authenticate(slot, PR_TRUE, cx);
198
0
    if (rv != SECSuccess)
199
0
        goto loser;
200
201
    /* Find the key to use */
202
0
    pKeyID = keyid;
203
0
    if (pKeyID->len == 0) {
204
0
        int keySize = PK11_GetBestKeyLength(slot, type);
205
0
        pKeyID = &keyIDItem;
206
207
        /* put in a course lock to prevent a race between not finding the
208
         * key and creating  one.
209
         */
210
211
0
        if (pk11sdrLock)
212
0
            PR_Lock(pk11sdrLock);
213
214
        /* Try to find the key */
215
0
        key = PK11_FindFixedKey(slot, type, pKeyID, cx);
216
217
        /* If the default key doesn't exist yet, try to create it */
218
0
        if (!key)
219
0
            key = PK11_TokenKeyGen(slot, type, 0, keySize, pKeyID, PR_TRUE, cx);
220
0
        if (pk11sdrLock)
221
0
            PR_Unlock(pk11sdrLock);
222
0
    } else {
223
0
        key = PK11_FindFixedKey(slot, type, pKeyID, cx);
224
0
    }
225
226
0
    if (!key) {
227
0
        rv = SECFailure;
228
0
        goto loser;
229
0
    }
230
231
0
    params = PK11_GenerateNewParam(type, key);
232
0
    if (!params) {
233
0
        rv = SECFailure;
234
0
        goto loser;
235
0
    }
236
237
0
    ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params);
238
0
    if (!ctx) {
239
0
        rv = SECFailure;
240
0
        goto loser;
241
0
    }
242
243
0
    rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData);
244
0
    if (rv != SECSuccess)
245
0
        goto loser;
246
247
0
    sdrResult.data.len = paddedData.len;
248
0
    sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len);
249
250
0
    rv = PK11_CipherOp(ctx, sdrResult.data.data, (int *)&sdrResult.data.len, sdrResult.data.len,
251
0
                       paddedData.data, paddedData.len);
252
0
    if (rv != SECSuccess)
253
0
        goto loser;
254
255
0
    PK11_Finalize(ctx);
256
257
0
    sdrResult.keyid = *pKeyID;
258
259
0
    algtag = SECOID_FindOIDByMechanism(type)->offset;
260
0
    rv = PK11_ParamToAlgid(algtag, params, arena, &sdrResult.alg);
261
0
    if (rv != SECSuccess)
262
0
        goto loser;
263
264
0
    if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) {
265
0
        rv = SECFailure;
266
0
        goto loser;
267
0
    }
268
269
0
loser:
270
0
    SECITEM_ZfreeItem(&paddedData, PR_FALSE);
271
0
    if (arena)
272
0
        PORT_FreeArena(arena, PR_TRUE);
273
0
    if (ctx)
274
0
        PK11_DestroyContext(ctx, PR_TRUE);
275
0
    if (params)
276
0
        SECITEM_ZfreeItem(params, PR_TRUE);
277
0
    if (key)
278
0
        PK11_FreeSymKey(key);
279
0
    if (slot && !aSlot)
280
0
        PK11_FreeSlot(slot);
281
282
0
    return rv;
283
0
}
284
285
/* decrypt a block */
286
static SECStatus
287
pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena,
288
            CK_MECHANISM_TYPE type, PK11SymKey *key,
289
            SECItem *params, SECItem *in, SECItem *result)
290
0
{
291
0
    PK11Context *ctx = 0;
292
0
    SECItem paddedResult;
293
0
    SECStatus rv;
294
295
0
    paddedResult.len = 0;
296
0
    paddedResult.data = 0;
297
298
0
    ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params);
299
0
    if (!ctx) {
300
0
        rv = SECFailure;
301
0
        goto loser;
302
0
    }
303
304
0
    paddedResult.len = in->len;
305
0
    paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len);
306
307
0
    rv = PK11_CipherOp(ctx, paddedResult.data,
308
0
                       (int *)&paddedResult.len, paddedResult.len,
309
0
                       in->data, in->len);
310
0
    if (rv != SECSuccess)
311
0
        goto loser;
312
313
0
    PK11_Finalize(ctx);
314
315
    /* Remove the padding */
316
0
    rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result);
317
0
    if (rv)
318
0
        goto loser;
319
320
0
loser:
321
0
    if (ctx)
322
0
        PK11_DestroyContext(ctx, PR_TRUE);
323
0
    return rv;
324
0
}
325
326
/*
327
 * PK11SDR_Decrypt
328
 *  Decrypt a block of data produced by PK11SDR_EncryptWithMechanism. The key
329
 *  used is identified by the keyid field within the input.
330
 */
331
SECStatus
332
PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx)
333
0
{
334
0
    SECStatus rv = SECSuccess;
335
0
    PK11SlotInfo *slot = 0;
336
0
    PK11SymKey *key = 0;
337
0
    CK_MECHANISM_TYPE type;
338
0
    SDRResult sdrResult;
339
0
    SECItem *params = 0;
340
0
    SECItem possibleResult = { 0, NULL, 0 };
341
0
    PLArenaPool *arena = 0;
342
0
    SECOidTag algtag;
343
344
0
    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
345
0
    if (!arena) {
346
0
        rv = SECFailure;
347
0
        goto loser;
348
0
    }
349
350
    /* Decode the incoming data */
351
0
    memset(&sdrResult, 0, sizeof sdrResult);
352
0
    rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data);
353
0
    if (rv != SECSuccess)
354
0
        goto loser; /* Invalid format */
355
356
    /* Find the slot and key for the given keyid */
357
0
    slot = PK11_GetInternalKeySlot();
358
0
    if (!slot) {
359
0
        rv = SECFailure;
360
0
        goto loser;
361
0
    }
362
363
0
    rv = PK11_Authenticate(slot, PR_TRUE, cx);
364
0
    if (rv != SECSuccess)
365
0
        goto loser;
366
367
    /* Get the parameter values from the data */
368
0
    params = PK11_ParamFromAlgid(&sdrResult.alg);
369
0
    if (!params) {
370
0
        rv = SECFailure;
371
0
        goto loser;
372
0
    }
373
374
0
    algtag = SECOID_GetAlgorithmTag(&sdrResult.alg);
375
0
    type = PK11_AlgtagToMechanism(algtag);
376
0
    key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx);
377
0
    if (!key) {
378
0
        rv = SECFailure;
379
0
    } else {
380
0
        rv = pk11Decrypt(slot, arena, type, key, params,
381
0
                         &sdrResult.data, result);
382
0
    }
383
384
    /*
385
     * if the pad value was too small (1 or 2), then it's statistically
386
     * 'likely' that (1 in 256) that we may not have the correct key.
387
     * Check the other keys for a better match. If we find none, use
388
     * this result.
389
     */
390
0
    if (rv == SECWouldBlock) {
391
0
        possibleResult = *result;
392
0
    }
393
394
    /*
395
     * handle the case where your key indicies may have been broken
396
     */
397
0
    if (rv != SECSuccess) {
398
0
        PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx);
399
0
        PK11SymKey *testKey = NULL;
400
0
        PK11SymKey *nextKey = NULL;
401
402
0
        for (testKey = keyList; testKey;
403
0
             testKey = PK11_GetNextSymKey(testKey)) {
404
0
            if (PK11_GetSymKeyType(testKey) != PK11_GetKeyType(type, 0)) {
405
0
                continue;
406
0
            }
407
408
0
            rv = pk11Decrypt(slot, arena, type, testKey, params,
409
0
                             &sdrResult.data, result);
410
0
            if (rv == SECSuccess) {
411
0
                break;
412
0
            }
413
            /* found a close match. If it's our first remember it */
414
0
            if (rv == SECWouldBlock) {
415
0
                if (possibleResult.data) {
416
                    /* this is unlikely but possible. If we hit this condition,
417
                     * we have no way of knowing which possibility to prefer.
418
                     * in this case we just match the key the application
419
                     * thought was the right one */
420
0
                    SECITEM_ZfreeItem(result, PR_FALSE);
421
0
                } else {
422
0
                    possibleResult = *result;
423
0
                }
424
0
            }
425
0
        }
426
427
        /* free the list */
428
0
        for (testKey = keyList; testKey; testKey = nextKey) {
429
0
            nextKey = PK11_GetNextSymKey(testKey);
430
0
            PK11_FreeSymKey(testKey);
431
0
        }
432
0
    }
433
434
    /* we didn't find a better key, use the one with a small pad value */
435
0
    if ((rv != SECSuccess) && (possibleResult.data)) {
436
0
        *result = possibleResult;
437
0
        possibleResult.data = NULL;
438
0
        rv = SECSuccess;
439
0
    }
440
441
0
loser:
442
0
    if (arena)
443
0
        PORT_FreeArena(arena, PR_TRUE);
444
0
    if (key)
445
0
        PK11_FreeSymKey(key);
446
0
    if (params)
447
0
        SECITEM_ZfreeItem(params, PR_TRUE);
448
0
    if (slot)
449
0
        PK11_FreeSlot(slot);
450
0
    if (possibleResult.data)
451
0
        SECITEM_ZfreeItem(&possibleResult, PR_FALSE);
452
453
0
    return rv;
454
0
}