1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/pk11wrap/pk11sdr.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,377 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "seccomon.h" 1.9 +#include "secoid.h" 1.10 +#include "secasn1.h" 1.11 +#include "pkcs11.h" 1.12 +#include "pk11func.h" 1.13 +#include "pk11sdr.h" 1.14 + 1.15 +/* 1.16 + * Data structure and template for encoding the result of an SDR operation 1.17 + * This is temporary. It should include the algorithm ID of the encryption mechanism 1.18 + */ 1.19 +struct SDRResult 1.20 +{ 1.21 + SECItem keyid; 1.22 + SECAlgorithmID alg; 1.23 + SECItem data; 1.24 +}; 1.25 +typedef struct SDRResult SDRResult; 1.26 + 1.27 +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) 1.28 + 1.29 +static SEC_ASN1Template template[] = { 1.30 + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, 1.31 + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, 1.32 + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), 1.33 + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, 1.34 + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, 1.35 + { 0 } 1.36 +}; 1.37 + 1.38 +static unsigned char keyID[] = { 1.39 + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1.40 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 1.41 +}; 1.42 + 1.43 +static SECItem keyIDItem = { 1.44 + 0, 1.45 + keyID, 1.46 + sizeof keyID 1.47 +}; 1.48 + 1.49 +/* local utility function for padding an incoming data block 1.50 + * to the mechanism block size. 1.51 + */ 1.52 +static SECStatus 1.53 +padBlock(SECItem *data, int blockSize, SECItem *result) 1.54 +{ 1.55 + SECStatus rv = SECSuccess; 1.56 + int padLength; 1.57 + unsigned int i; 1.58 + 1.59 + result->data = 0; 1.60 + result->len = 0; 1.61 + 1.62 + /* This algorithm always adds to the block (to indicate the number 1.63 + * of pad bytes). So allocate a block large enough. 1.64 + */ 1.65 + padLength = blockSize - (data->len % blockSize); 1.66 + result->len = data->len + padLength; 1.67 + result->data = (unsigned char *)PORT_Alloc(result->len); 1.68 + 1.69 + /* Copy the data */ 1.70 + PORT_Memcpy(result->data, data->data, data->len); 1.71 + 1.72 + /* Add the pad values */ 1.73 + for(i = data->len; i < result->len; i++) 1.74 + result->data[i] = (unsigned char)padLength; 1.75 + 1.76 + return rv; 1.77 +} 1.78 + 1.79 +static SECStatus 1.80 +unpadBlock(SECItem *data, int blockSize, SECItem *result) 1.81 +{ 1.82 + SECStatus rv = SECSuccess; 1.83 + int padLength; 1.84 + unsigned int i; 1.85 + 1.86 + result->data = 0; 1.87 + result->len = 0; 1.88 + 1.89 + /* Remove the padding from the end if the input data */ 1.90 + if (data->len == 0 || data->len % blockSize != 0) { rv = SECFailure; goto loser; } 1.91 + 1.92 + padLength = data->data[data->len-1]; 1.93 + if (padLength > blockSize) { rv = SECFailure; goto loser; } 1.94 + 1.95 + /* verify padding */ 1.96 + for (i=data->len - padLength; i < data->len; i++) { 1.97 + if (data->data[i] != padLength) { 1.98 + rv = SECFailure; 1.99 + goto loser; 1.100 + } 1.101 + } 1.102 + 1.103 + result->len = data->len - padLength; 1.104 + result->data = (unsigned char *)PORT_Alloc(result->len); 1.105 + if (!result->data) { rv = SECFailure; goto loser; } 1.106 + 1.107 + PORT_Memcpy(result->data, data->data, result->len); 1.108 + 1.109 + if (padLength < 2) { 1.110 + return SECWouldBlock; 1.111 + } 1.112 + 1.113 +loser: 1.114 + return rv; 1.115 +} 1.116 + 1.117 +static PRLock *pk11sdrLock = NULL; 1.118 + 1.119 +void 1.120 +pk11sdr_Init (void) 1.121 +{ 1.122 + pk11sdrLock = PR_NewLock(); 1.123 +} 1.124 + 1.125 +void 1.126 +pk11sdr_Shutdown(void) 1.127 +{ 1.128 + if (pk11sdrLock) { 1.129 + PR_DestroyLock(pk11sdrLock); 1.130 + pk11sdrLock = NULL; 1.131 + } 1.132 +} 1.133 + 1.134 +/* 1.135 + * PK11SDR_Encrypt 1.136 + * Encrypt a block of data using the symmetric key identified. The result 1.137 + * is an ASN.1 (DER) encoded block of keyid, params and data. 1.138 + */ 1.139 +SECStatus 1.140 +PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx) 1.141 +{ 1.142 + SECStatus rv = SECSuccess; 1.143 + PK11SlotInfo *slot = 0; 1.144 + PK11SymKey *key = 0; 1.145 + SECItem *params = 0; 1.146 + PK11Context *ctx = 0; 1.147 + CK_MECHANISM_TYPE type; 1.148 + SDRResult sdrResult; 1.149 + SECItem paddedData; 1.150 + SECItem *pKeyID; 1.151 + PLArenaPool *arena = 0; 1.152 + 1.153 + /* Initialize */ 1.154 + paddedData.len = 0; 1.155 + paddedData.data = 0; 1.156 + 1.157 + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); 1.158 + if (!arena) { rv = SECFailure; goto loser; } 1.159 + 1.160 + /* 1. Locate the requested keyid, or the default key (which has a keyid) 1.161 + * 2. Create an encryption context 1.162 + * 3. Encrypt 1.163 + * 4. Encode the results (using ASN.1) 1.164 + */ 1.165 + 1.166 + slot = PK11_GetInternalKeySlot(); 1.167 + if (!slot) { rv = SECFailure; goto loser; } 1.168 + 1.169 + /* Use triple-DES */ 1.170 + type = CKM_DES3_CBC; 1.171 + 1.172 + /* 1.173 + * Login to the internal token before we look for the key, otherwise we 1.174 + * won't find it. 1.175 + */ 1.176 + rv = PK11_Authenticate(slot, PR_TRUE, cx); 1.177 + if (rv != SECSuccess) goto loser; 1.178 + 1.179 + /* Find the key to use */ 1.180 + pKeyID = keyid; 1.181 + if (pKeyID->len == 0) { 1.182 + pKeyID = &keyIDItem; /* Use default value */ 1.183 + 1.184 + /* put in a course lock to prevent a race between not finding the 1.185 + * key and creating one. 1.186 + */ 1.187 + 1.188 + if (pk11sdrLock) PR_Lock(pk11sdrLock); 1.189 + 1.190 + /* Try to find the key */ 1.191 + key = PK11_FindFixedKey(slot, type, pKeyID, cx); 1.192 + 1.193 + /* If the default key doesn't exist yet, try to create it */ 1.194 + if (!key) key = PK11_GenDES3TokenKey(slot, pKeyID, cx); 1.195 + if (pk11sdrLock) PR_Unlock(pk11sdrLock); 1.196 + } else { 1.197 + key = PK11_FindFixedKey(slot, type, pKeyID, cx); 1.198 + } 1.199 + 1.200 + if (!key) { rv = SECFailure; goto loser; } 1.201 + 1.202 + params = PK11_GenerateNewParam(type, key); 1.203 + if (!params) { rv = SECFailure; goto loser; } 1.204 + 1.205 + ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params); 1.206 + if (!ctx) { rv = SECFailure; goto loser; } 1.207 + 1.208 + rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData); 1.209 + if (rv != SECSuccess) goto loser; 1.210 + 1.211 + sdrResult.data.len = paddedData.len; 1.212 + sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len); 1.213 + 1.214 + rv = PK11_CipherOp(ctx, sdrResult.data.data, (int*)&sdrResult.data.len, sdrResult.data.len, 1.215 + paddedData.data, paddedData.len); 1.216 + if (rv != SECSuccess) goto loser; 1.217 + 1.218 + PK11_Finalize(ctx); 1.219 + 1.220 + sdrResult.keyid = *pKeyID; 1.221 + 1.222 + rv = PK11_ParamToAlgid(SEC_OID_DES_EDE3_CBC, params, arena, &sdrResult.alg); 1.223 + if (rv != SECSuccess) goto loser; 1.224 + 1.225 + if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) { rv = SECFailure; goto loser; } 1.226 + 1.227 +loser: 1.228 + SECITEM_ZfreeItem(&paddedData, PR_FALSE); 1.229 + if (arena) PORT_FreeArena(arena, PR_TRUE); 1.230 + if (ctx) PK11_DestroyContext(ctx, PR_TRUE); 1.231 + if (params) SECITEM_ZfreeItem(params, PR_TRUE); 1.232 + if (key) PK11_FreeSymKey(key); 1.233 + if (slot) PK11_FreeSlot(slot); 1.234 + 1.235 + return rv; 1.236 +} 1.237 + 1.238 +/* decrypt a block */ 1.239 +static SECStatus 1.240 +pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, 1.241 + CK_MECHANISM_TYPE type, PK11SymKey *key, 1.242 + SECItem *params, SECItem *in, SECItem *result) 1.243 +{ 1.244 + PK11Context *ctx = 0; 1.245 + SECItem paddedResult; 1.246 + SECStatus rv; 1.247 + 1.248 + paddedResult.len = 0; 1.249 + paddedResult.data = 0; 1.250 + 1.251 + ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); 1.252 + if (!ctx) { rv = SECFailure; goto loser; } 1.253 + 1.254 + paddedResult.len = in->len; 1.255 + paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len); 1.256 + 1.257 + rv = PK11_CipherOp(ctx, paddedResult.data, 1.258 + (int*)&paddedResult.len, paddedResult.len, 1.259 + in->data, in->len); 1.260 + if (rv != SECSuccess) goto loser; 1.261 + 1.262 + PK11_Finalize(ctx); 1.263 + 1.264 + /* Remove the padding */ 1.265 + rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); 1.266 + if (rv) goto loser; 1.267 + 1.268 +loser: 1.269 + if (ctx) PK11_DestroyContext(ctx, PR_TRUE); 1.270 + return rv; 1.271 +} 1.272 + 1.273 +/* 1.274 + * PK11SDR_Decrypt 1.275 + * Decrypt a block of data produced by PK11SDR_Encrypt. The key used is identified 1.276 + * by the keyid field within the input. 1.277 + */ 1.278 +SECStatus 1.279 +PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx) 1.280 +{ 1.281 + SECStatus rv = SECSuccess; 1.282 + PK11SlotInfo *slot = 0; 1.283 + PK11SymKey *key = 0; 1.284 + CK_MECHANISM_TYPE type; 1.285 + SDRResult sdrResult; 1.286 + SECItem *params = 0; 1.287 + SECItem possibleResult = { 0, NULL, 0 }; 1.288 + PLArenaPool *arena = 0; 1.289 + 1.290 + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); 1.291 + if (!arena) { rv = SECFailure; goto loser; } 1.292 + 1.293 + /* Decode the incoming data */ 1.294 + memset(&sdrResult, 0, sizeof sdrResult); 1.295 + rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data); 1.296 + if (rv != SECSuccess) goto loser; /* Invalid format */ 1.297 + 1.298 + /* Find the slot and key for the given keyid */ 1.299 + slot = PK11_GetInternalKeySlot(); 1.300 + if (!slot) { rv = SECFailure; goto loser; } 1.301 + 1.302 + rv = PK11_Authenticate(slot, PR_TRUE, cx); 1.303 + if (rv != SECSuccess) goto loser; 1.304 + 1.305 + /* Get the parameter values from the data */ 1.306 + params = PK11_ParamFromAlgid(&sdrResult.alg); 1.307 + if (!params) { rv = SECFailure; goto loser; } 1.308 + 1.309 + /* Use triple-DES (Should look up the algorithm) */ 1.310 + type = CKM_DES3_CBC; 1.311 + key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); 1.312 + if (!key) { 1.313 + rv = SECFailure; 1.314 + } else { 1.315 + rv = pk11Decrypt(slot, arena, type, key, params, 1.316 + &sdrResult.data, result); 1.317 + } 1.318 + 1.319 + /* 1.320 + * if the pad value was too small (1 or 2), then it's statistically 1.321 + * 'likely' that (1 in 256) that we may not have the correct key. 1.322 + * Check the other keys for a better match. If we find none, use 1.323 + * this result. 1.324 + */ 1.325 + if (rv == SECWouldBlock) { 1.326 + possibleResult = *result; 1.327 + } 1.328 + 1.329 + /* 1.330 + * handle the case where your key indicies may have been broken 1.331 + */ 1.332 + if (rv != SECSuccess) { 1.333 + PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); 1.334 + PK11SymKey *testKey = NULL; 1.335 + PK11SymKey *nextKey = NULL; 1.336 + 1.337 + for (testKey = keyList; testKey; 1.338 + testKey = PK11_GetNextSymKey(testKey)) { 1.339 + rv = pk11Decrypt(slot, arena, type, testKey, params, 1.340 + &sdrResult.data, result); 1.341 + if (rv == SECSuccess) { 1.342 + break; 1.343 + } 1.344 + /* found a close match. If it's our first remember it */ 1.345 + if (rv == SECWouldBlock) { 1.346 + if (possibleResult.data) { 1.347 + /* this is unlikely but possible. If we hit this condition, 1.348 + * we have no way of knowing which possibility to prefer. 1.349 + * in this case we just match the key the application 1.350 + * thought was the right one */ 1.351 + SECITEM_ZfreeItem(result, PR_FALSE); 1.352 + } else { 1.353 + possibleResult = *result; 1.354 + } 1.355 + } 1.356 + } 1.357 + 1.358 + /* free the list */ 1.359 + for (testKey = keyList; testKey; testKey = nextKey) { 1.360 + nextKey = PK11_GetNextSymKey(testKey); 1.361 + PK11_FreeSymKey(testKey); 1.362 + } 1.363 + } 1.364 + 1.365 + /* we didn't find a better key, use the one with a small pad value */ 1.366 + if ((rv != SECSuccess) && (possibleResult.data)) { 1.367 + *result = possibleResult; 1.368 + possibleResult.data = NULL; 1.369 + rv = SECSuccess; 1.370 + } 1.371 + 1.372 +loser: 1.373 + if (arena) PORT_FreeArena(arena, PR_TRUE); 1.374 + if (key) PK11_FreeSymKey(key); 1.375 + if (params) SECITEM_ZfreeItem(params, PR_TRUE); 1.376 + if (slot) PK11_FreeSlot(slot); 1.377 + if (possibleResult.data) SECITEM_ZfreeItem(&possibleResult, PR_FALSE); 1.378 + 1.379 + return rv; 1.380 +}