pk11sdr.c (12032B)
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 { 51 SECStatus rv = SECSuccess; 52 int padLength; 53 unsigned int i; 54 55 result->data = 0; 56 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 padLength = blockSize - (data->len % blockSize); 62 result->len = data->len + padLength; 63 result->data = (unsigned char *)PORT_Alloc(result->len); 64 65 /* Copy the data */ 66 PORT_Memcpy(result->data, data->data, data->len); 67 68 /* Add the pad values */ 69 for (i = data->len; i < result->len; i++) 70 result->data[i] = (unsigned char)padLength; 71 72 return rv; 73 } 74 75 static SECStatus 76 unpadBlock(SECItem *data, int blockSize, SECItem *result) 77 { 78 SECStatus rv = SECSuccess; 79 int padLength; 80 unsigned int i; 81 82 result->data = 0; 83 result->len = 0; 84 85 /* Remove the padding from the end if the input data */ 86 if (data->len == 0 || data->len % blockSize != 0) { 87 rv = SECFailure; 88 goto loser; 89 } 90 91 padLength = data->data[data->len - 1]; 92 if (padLength > blockSize) { 93 rv = SECFailure; 94 goto loser; 95 } 96 97 /* verify padding */ 98 for (i = data->len - padLength; i < data->len; i++) { 99 if (data->data[i] != padLength) { 100 rv = SECFailure; 101 goto loser; 102 } 103 } 104 105 result->len = data->len - padLength; 106 result->data = (unsigned char *)PORT_Alloc(result->len); 107 if (!result->data) { 108 rv = SECFailure; 109 goto loser; 110 } 111 112 PORT_Memcpy(result->data, data->data, result->len); 113 114 if (padLength < 2) { 115 return SECWouldBlock; 116 } 117 118 loser: 119 return rv; 120 } 121 122 static PRLock *pk11sdrLock = NULL; 123 124 void 125 pk11sdr_Init(void) 126 { 127 pk11sdrLock = PR_NewLock(); 128 } 129 130 void 131 pk11sdr_Shutdown(void) 132 { 133 if (pk11sdrLock) { 134 PR_DestroyLock(pk11sdrLock); 135 pk11sdrLock = NULL; 136 } 137 } 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 { 146 return PK11SDR_EncryptWithMechanism(NULL, keyid, CKM_DES3_CBC, data, result, cx); 147 } 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 { 158 SECStatus rv = SECSuccess; 159 PK11SymKey *key = 0; 160 SECItem *params = 0; 161 PK11Context *ctx = 0; 162 SDRResult sdrResult; 163 SECItem paddedData; 164 SECItem *pKeyID; 165 PLArenaPool *arena = 0; 166 SECOidTag algtag; 167 PK11SlotInfo *aSlot = slot; 168 169 /* Initialize */ 170 paddedData.len = 0; 171 paddedData.data = 0; 172 173 arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); 174 if (!arena) { 175 rv = SECFailure; 176 goto loser; 177 } 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 if (!slot) { 186 slot = PK11_GetInternalKeySlot(); 187 if (!slot) { 188 rv = SECFailure; 189 goto loser; 190 } 191 } 192 193 /* 194 * Login to the internal token before we look for the key, otherwise we 195 * won't find it. 196 */ 197 rv = PK11_Authenticate(slot, PR_TRUE, cx); 198 if (rv != SECSuccess) 199 goto loser; 200 201 /* Find the key to use */ 202 pKeyID = keyid; 203 if (pKeyID->len == 0) { 204 int keySize = PK11_GetBestKeyLength(slot, type); 205 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 if (pk11sdrLock) 212 PR_Lock(pk11sdrLock); 213 214 /* Try to find the key */ 215 key = PK11_FindFixedKey(slot, type, pKeyID, cx); 216 217 /* If the default key doesn't exist yet, try to create it */ 218 if (!key) 219 key = PK11_TokenKeyGen(slot, type, 0, keySize, pKeyID, PR_TRUE, cx); 220 if (pk11sdrLock) 221 PR_Unlock(pk11sdrLock); 222 } else { 223 key = PK11_FindFixedKey(slot, type, pKeyID, cx); 224 } 225 226 if (!key) { 227 rv = SECFailure; 228 goto loser; 229 } 230 231 params = PK11_GenerateNewParam(type, key); 232 if (!params) { 233 rv = SECFailure; 234 goto loser; 235 } 236 237 ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params); 238 if (!ctx) { 239 rv = SECFailure; 240 goto loser; 241 } 242 243 rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData); 244 if (rv != SECSuccess) 245 goto loser; 246 247 sdrResult.data.len = paddedData.len; 248 sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len); 249 250 rv = PK11_CipherOp(ctx, sdrResult.data.data, (int *)&sdrResult.data.len, sdrResult.data.len, 251 paddedData.data, paddedData.len); 252 if (rv != SECSuccess) 253 goto loser; 254 255 PK11_Finalize(ctx); 256 257 sdrResult.keyid = *pKeyID; 258 259 algtag = SECOID_FindOIDByMechanism(type)->offset; 260 rv = PK11_ParamToAlgid(algtag, params, arena, &sdrResult.alg); 261 if (rv != SECSuccess) 262 goto loser; 263 264 if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) { 265 rv = SECFailure; 266 goto loser; 267 } 268 269 loser: 270 SECITEM_ZfreeItem(&paddedData, PR_FALSE); 271 if (arena) 272 PORT_FreeArena(arena, PR_TRUE); 273 if (ctx) 274 PK11_DestroyContext(ctx, PR_TRUE); 275 if (params) 276 SECITEM_ZfreeItem(params, PR_TRUE); 277 if (key) 278 PK11_FreeSymKey(key); 279 if (slot && !aSlot) 280 PK11_FreeSlot(slot); 281 282 return rv; 283 } 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 { 291 PK11Context *ctx = 0; 292 SECItem paddedResult; 293 SECStatus rv; 294 295 paddedResult.len = 0; 296 paddedResult.data = 0; 297 298 ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); 299 if (!ctx) { 300 rv = SECFailure; 301 goto loser; 302 } 303 304 paddedResult.len = in->len; 305 paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len); 306 307 rv = PK11_CipherOp(ctx, paddedResult.data, 308 (int *)&paddedResult.len, paddedResult.len, 309 in->data, in->len); 310 if (rv != SECSuccess) 311 goto loser; 312 313 PK11_Finalize(ctx); 314 315 /* Remove the padding */ 316 rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); 317 if (rv) 318 goto loser; 319 320 loser: 321 if (ctx) 322 PK11_DestroyContext(ctx, PR_TRUE); 323 return rv; 324 } 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 { 334 SECStatus rv = SECSuccess; 335 PK11SlotInfo *slot = 0; 336 PK11SymKey *key = 0; 337 CK_MECHANISM_TYPE type; 338 SDRResult sdrResult; 339 SECItem *params = 0; 340 SECItem possibleResult = { 0, NULL, 0 }; 341 PLArenaPool *arena = 0; 342 SECOidTag algtag; 343 344 arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); 345 if (!arena) { 346 rv = SECFailure; 347 goto loser; 348 } 349 350 /* Decode the incoming data */ 351 memset(&sdrResult, 0, sizeof sdrResult); 352 rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data); 353 if (rv != SECSuccess) 354 goto loser; /* Invalid format */ 355 356 /* Find the slot and key for the given keyid */ 357 slot = PK11_GetInternalKeySlot(); 358 if (!slot) { 359 rv = SECFailure; 360 goto loser; 361 } 362 363 rv = PK11_Authenticate(slot, PR_TRUE, cx); 364 if (rv != SECSuccess) 365 goto loser; 366 367 /* Get the parameter values from the data */ 368 params = PK11_ParamFromAlgid(&sdrResult.alg); 369 if (!params) { 370 rv = SECFailure; 371 goto loser; 372 } 373 374 algtag = SECOID_GetAlgorithmTag(&sdrResult.alg); 375 type = PK11_AlgtagToMechanism(algtag); 376 key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); 377 if (!key) { 378 rv = SECFailure; 379 } else { 380 rv = pk11Decrypt(slot, arena, type, key, params, 381 &sdrResult.data, result); 382 } 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 if (rv == SECWouldBlock) { 391 possibleResult = *result; 392 } 393 394 /* 395 * handle the case where your key indicies may have been broken 396 */ 397 if (rv != SECSuccess) { 398 PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); 399 PK11SymKey *testKey = NULL; 400 PK11SymKey *nextKey = NULL; 401 402 for (testKey = keyList; testKey; 403 testKey = PK11_GetNextSymKey(testKey)) { 404 if (PK11_GetSymKeyType(testKey) != PK11_GetKeyType(type, 0)) { 405 continue; 406 } 407 408 rv = pk11Decrypt(slot, arena, type, testKey, params, 409 &sdrResult.data, result); 410 if (rv == SECSuccess) { 411 break; 412 } 413 /* found a close match. If it's our first remember it */ 414 if (rv == SECWouldBlock) { 415 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 SECITEM_ZfreeItem(result, PR_FALSE); 421 } else { 422 possibleResult = *result; 423 } 424 } 425 } 426 427 /* free the list */ 428 for (testKey = keyList; testKey; testKey = nextKey) { 429 nextKey = PK11_GetNextSymKey(testKey); 430 PK11_FreeSymKey(testKey); 431 } 432 } 433 434 /* we didn't find a better key, use the one with a small pad value */ 435 if ((rv != SECSuccess) && (possibleResult.data)) { 436 *result = possibleResult; 437 possibleResult.data = NULL; 438 rv = SECSuccess; 439 } 440 441 loser: 442 if (arena) 443 PORT_FreeArena(arena, PR_TRUE); 444 if (key) 445 PK11_FreeSymKey(key); 446 if (params) 447 SECITEM_ZfreeItem(params, PR_TRUE); 448 if (slot) 449 PK11_FreeSlot(slot); 450 if (possibleResult.data) 451 SECITEM_ZfreeItem(&possibleResult, PR_FALSE); 452 453 return rv; 454 }