security/nss/lib/pk11wrap/pk11sdr.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial