andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "seccomon.h" andre@0: #include "secoid.h" andre@0: #include "secasn1.h" andre@0: #include "pkcs11.h" andre@0: #include "pk11func.h" andre@0: #include "pk11sdr.h" andre@0: andre@0: /* andre@0: * Data structure and template for encoding the result of an SDR operation andre@0: * This is temporary. It should include the algorithm ID of the encryption mechanism andre@0: */ andre@0: struct SDRResult andre@0: { andre@0: SECItem keyid; andre@0: SECAlgorithmID alg; andre@0: SECItem data; andre@0: }; andre@0: typedef struct SDRResult SDRResult; andre@0: andre@0: SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) andre@0: andre@0: static SEC_ASN1Template template[] = { andre@0: { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, andre@0: { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, andre@0: { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), andre@0: SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, andre@0: { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, andre@0: { 0 } andre@0: }; andre@0: andre@0: static unsigned char keyID[] = { andre@0: 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, andre@0: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 andre@0: }; andre@0: andre@0: static SECItem keyIDItem = { andre@0: 0, andre@0: keyID, andre@0: sizeof keyID andre@0: }; andre@0: andre@0: /* local utility function for padding an incoming data block andre@0: * to the mechanism block size. andre@0: */ andre@0: static SECStatus andre@0: padBlock(SECItem *data, int blockSize, SECItem *result) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: int padLength; andre@0: unsigned int i; andre@0: andre@0: result->data = 0; andre@0: result->len = 0; andre@0: andre@0: /* This algorithm always adds to the block (to indicate the number andre@0: * of pad bytes). So allocate a block large enough. andre@0: */ andre@0: padLength = blockSize - (data->len % blockSize); andre@0: result->len = data->len + padLength; andre@0: result->data = (unsigned char *)PORT_Alloc(result->len); andre@0: andre@0: /* Copy the data */ andre@0: PORT_Memcpy(result->data, data->data, data->len); andre@0: andre@0: /* Add the pad values */ andre@0: for(i = data->len; i < result->len; i++) andre@0: result->data[i] = (unsigned char)padLength; andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: static SECStatus andre@0: unpadBlock(SECItem *data, int blockSize, SECItem *result) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: int padLength; andre@0: unsigned int i; andre@0: andre@0: result->data = 0; andre@0: result->len = 0; andre@0: andre@0: /* Remove the padding from the end if the input data */ andre@0: if (data->len == 0 || data->len % blockSize != 0) { rv = SECFailure; goto loser; } andre@0: andre@0: padLength = data->data[data->len-1]; andre@0: if (padLength > blockSize) { rv = SECFailure; goto loser; } andre@0: andre@0: /* verify padding */ andre@0: for (i=data->len - padLength; i < data->len; i++) { andre@0: if (data->data[i] != padLength) { andre@0: rv = SECFailure; andre@0: goto loser; andre@0: } andre@0: } andre@0: andre@0: result->len = data->len - padLength; andre@0: result->data = (unsigned char *)PORT_Alloc(result->len); andre@0: if (!result->data) { rv = SECFailure; goto loser; } andre@0: andre@0: PORT_Memcpy(result->data, data->data, result->len); andre@0: andre@0: if (padLength < 2) { andre@0: return SECWouldBlock; andre@0: } andre@0: andre@0: loser: andre@0: return rv; andre@0: } andre@0: andre@0: static PRLock *pk11sdrLock = NULL; andre@0: andre@0: void andre@0: pk11sdr_Init (void) andre@0: { andre@0: pk11sdrLock = PR_NewLock(); andre@0: } andre@0: andre@0: void andre@0: pk11sdr_Shutdown(void) andre@0: { andre@0: if (pk11sdrLock) { andre@0: PR_DestroyLock(pk11sdrLock); andre@0: pk11sdrLock = NULL; andre@0: } andre@0: } andre@0: andre@0: /* andre@0: * PK11SDR_Encrypt andre@0: * Encrypt a block of data using the symmetric key identified. The result andre@0: * is an ASN.1 (DER) encoded block of keyid, params and data. andre@0: */ andre@0: SECStatus andre@0: PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: PK11SlotInfo *slot = 0; andre@0: PK11SymKey *key = 0; andre@0: SECItem *params = 0; andre@0: PK11Context *ctx = 0; andre@0: CK_MECHANISM_TYPE type; andre@0: SDRResult sdrResult; andre@0: SECItem paddedData; andre@0: SECItem *pKeyID; andre@0: PLArenaPool *arena = 0; andre@0: andre@0: /* Initialize */ andre@0: paddedData.len = 0; andre@0: paddedData.data = 0; andre@0: andre@0: arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); andre@0: if (!arena) { rv = SECFailure; goto loser; } andre@0: andre@0: /* 1. Locate the requested keyid, or the default key (which has a keyid) andre@0: * 2. Create an encryption context andre@0: * 3. Encrypt andre@0: * 4. Encode the results (using ASN.1) andre@0: */ andre@0: andre@0: slot = PK11_GetInternalKeySlot(); andre@0: if (!slot) { rv = SECFailure; goto loser; } andre@0: andre@0: /* Use triple-DES */ andre@0: type = CKM_DES3_CBC; andre@0: andre@0: /* andre@0: * Login to the internal token before we look for the key, otherwise we andre@0: * won't find it. andre@0: */ andre@0: rv = PK11_Authenticate(slot, PR_TRUE, cx); andre@0: if (rv != SECSuccess) goto loser; andre@0: andre@0: /* Find the key to use */ andre@0: pKeyID = keyid; andre@0: if (pKeyID->len == 0) { andre@0: pKeyID = &keyIDItem; /* Use default value */ andre@0: andre@0: /* put in a course lock to prevent a race between not finding the andre@0: * key and creating one. andre@0: */ andre@0: andre@0: if (pk11sdrLock) PR_Lock(pk11sdrLock); andre@0: andre@0: /* Try to find the key */ andre@0: key = PK11_FindFixedKey(slot, type, pKeyID, cx); andre@0: andre@0: /* If the default key doesn't exist yet, try to create it */ andre@0: if (!key) key = PK11_GenDES3TokenKey(slot, pKeyID, cx); andre@0: if (pk11sdrLock) PR_Unlock(pk11sdrLock); andre@0: } else { andre@0: key = PK11_FindFixedKey(slot, type, pKeyID, cx); andre@0: } andre@0: andre@0: if (!key) { rv = SECFailure; goto loser; } andre@0: andre@0: params = PK11_GenerateNewParam(type, key); andre@0: if (!params) { rv = SECFailure; goto loser; } andre@0: andre@0: ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params); andre@0: if (!ctx) { rv = SECFailure; goto loser; } andre@0: andre@0: rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData); andre@0: if (rv != SECSuccess) goto loser; andre@0: andre@0: sdrResult.data.len = paddedData.len; andre@0: sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len); andre@0: andre@0: rv = PK11_CipherOp(ctx, sdrResult.data.data, (int*)&sdrResult.data.len, sdrResult.data.len, andre@0: paddedData.data, paddedData.len); andre@0: if (rv != SECSuccess) goto loser; andre@0: andre@0: PK11_Finalize(ctx); andre@0: andre@0: sdrResult.keyid = *pKeyID; andre@0: andre@0: rv = PK11_ParamToAlgid(SEC_OID_DES_EDE3_CBC, params, arena, &sdrResult.alg); andre@0: if (rv != SECSuccess) goto loser; andre@0: andre@0: if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) { rv = SECFailure; goto loser; } andre@0: andre@0: loser: andre@0: SECITEM_ZfreeItem(&paddedData, PR_FALSE); andre@0: if (arena) PORT_FreeArena(arena, PR_TRUE); andre@0: if (ctx) PK11_DestroyContext(ctx, PR_TRUE); andre@0: if (params) SECITEM_ZfreeItem(params, PR_TRUE); andre@0: if (key) PK11_FreeSymKey(key); andre@0: if (slot) PK11_FreeSlot(slot); andre@0: andre@0: return rv; andre@0: } andre@0: andre@0: /* decrypt a block */ andre@0: static SECStatus andre@0: pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, andre@0: CK_MECHANISM_TYPE type, PK11SymKey *key, andre@0: SECItem *params, SECItem *in, SECItem *result) andre@0: { andre@0: PK11Context *ctx = 0; andre@0: SECItem paddedResult; andre@0: SECStatus rv; andre@0: andre@0: paddedResult.len = 0; andre@0: paddedResult.data = 0; andre@0: andre@0: ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); andre@0: if (!ctx) { rv = SECFailure; goto loser; } andre@0: andre@0: paddedResult.len = in->len; andre@0: paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len); andre@0: andre@0: rv = PK11_CipherOp(ctx, paddedResult.data, andre@0: (int*)&paddedResult.len, paddedResult.len, andre@0: in->data, in->len); andre@0: if (rv != SECSuccess) goto loser; andre@0: andre@0: PK11_Finalize(ctx); andre@0: andre@0: /* Remove the padding */ andre@0: rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); andre@0: if (rv) goto loser; andre@0: andre@0: loser: andre@0: if (ctx) PK11_DestroyContext(ctx, PR_TRUE); andre@0: return rv; andre@0: } andre@0: andre@0: /* andre@0: * PK11SDR_Decrypt andre@0: * Decrypt a block of data produced by PK11SDR_Encrypt. The key used is identified andre@0: * by the keyid field within the input. andre@0: */ andre@0: SECStatus andre@0: PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx) andre@0: { andre@0: SECStatus rv = SECSuccess; andre@0: PK11SlotInfo *slot = 0; andre@0: PK11SymKey *key = 0; andre@0: CK_MECHANISM_TYPE type; andre@0: SDRResult sdrResult; andre@0: SECItem *params = 0; andre@0: SECItem possibleResult = { 0, NULL, 0 }; andre@0: PLArenaPool *arena = 0; andre@0: andre@0: arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); andre@0: if (!arena) { rv = SECFailure; goto loser; } andre@0: andre@0: /* Decode the incoming data */ andre@0: memset(&sdrResult, 0, sizeof sdrResult); andre@0: rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data); andre@0: if (rv != SECSuccess) goto loser; /* Invalid format */ andre@0: andre@0: /* Find the slot and key for the given keyid */ andre@0: slot = PK11_GetInternalKeySlot(); andre@0: if (!slot) { rv = SECFailure; goto loser; } andre@0: andre@0: rv = PK11_Authenticate(slot, PR_TRUE, cx); andre@0: if (rv != SECSuccess) goto loser; andre@0: andre@0: /* Get the parameter values from the data */ andre@0: params = PK11_ParamFromAlgid(&sdrResult.alg); andre@0: if (!params) { rv = SECFailure; goto loser; } andre@0: andre@0: /* Use triple-DES (Should look up the algorithm) */ andre@0: type = CKM_DES3_CBC; andre@0: key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); andre@0: if (!key) { andre@0: rv = SECFailure; andre@0: } else { andre@0: rv = pk11Decrypt(slot, arena, type, key, params, andre@0: &sdrResult.data, result); andre@0: } andre@0: andre@0: /* andre@0: * if the pad value was too small (1 or 2), then it's statistically andre@0: * 'likely' that (1 in 256) that we may not have the correct key. andre@0: * Check the other keys for a better match. If we find none, use andre@0: * this result. andre@0: */ andre@0: if (rv == SECWouldBlock) { andre@0: possibleResult = *result; andre@0: } andre@0: andre@0: /* andre@0: * handle the case where your key indicies may have been broken andre@0: */ andre@0: if (rv != SECSuccess) { andre@0: PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); andre@0: PK11SymKey *testKey = NULL; andre@0: PK11SymKey *nextKey = NULL; andre@0: andre@0: for (testKey = keyList; testKey; andre@0: testKey = PK11_GetNextSymKey(testKey)) { andre@0: rv = pk11Decrypt(slot, arena, type, testKey, params, andre@0: &sdrResult.data, result); andre@0: if (rv == SECSuccess) { andre@0: break; andre@0: } andre@0: /* found a close match. If it's our first remember it */ andre@0: if (rv == SECWouldBlock) { andre@0: if (possibleResult.data) { andre@0: /* this is unlikely but possible. If we hit this condition, andre@0: * we have no way of knowing which possibility to prefer. andre@0: * in this case we just match the key the application andre@0: * thought was the right one */ andre@0: SECITEM_ZfreeItem(result, PR_FALSE); andre@0: } else { andre@0: possibleResult = *result; andre@0: } andre@0: } andre@0: } andre@0: andre@0: /* free the list */ andre@0: for (testKey = keyList; testKey; testKey = nextKey) { andre@0: nextKey = PK11_GetNextSymKey(testKey); andre@0: PK11_FreeSymKey(testKey); andre@0: } andre@0: } andre@0: andre@0: /* we didn't find a better key, use the one with a small pad value */ andre@0: if ((rv != SECSuccess) && (possibleResult.data)) { andre@0: *result = possibleResult; andre@0: possibleResult.data = NULL; andre@0: rv = SECSuccess; andre@0: } andre@0: andre@0: loser: andre@0: if (arena) PORT_FreeArena(arena, PR_TRUE); andre@0: if (key) PK11_FreeSymKey(key); andre@0: if (params) SECITEM_ZfreeItem(params, PR_TRUE); andre@0: if (slot) PK11_FreeSlot(slot); andre@0: if (possibleResult.data) SECITEM_ZfreeItem(&possibleResult, PR_FALSE); andre@0: andre@0: return rv; andre@0: }