1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/utility/importer/nss_decryptor_system_nss.h" 6 7 #include <pk11pub.h> 8 #include <pk11sdr.h> 9 10 #include "base/basictypes.h" 11 #include "base/files/file_path.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/sys_string_conversions.h" 14 #include "crypto/nss_util.h" 15 16 NSSDecryptor::NSSDecryptor() : is_nss_initialized_(false), db_slot_(NULL) {} 17 NSSDecryptor::~NSSDecryptor() { 18 if (db_slot_) { 19 // Deliberately leave the user db open, just in case we need to open more 20 // than one, because there's an NSS bug with reopening user dbs. 21 // https://bugzilla.mozilla.org/show_bug.cgi?id=506140 22 // SECMOD_CloseUserDB(db_slot_); 23 PK11_FreeSlot(db_slot_); 24 } 25 } 26 27 bool NSSDecryptor::Init(const base::FilePath& dll_path, 28 const base::FilePath& db_path) { 29 crypto::EnsureNSSInit(); 30 is_nss_initialized_ = true; 31 const std::string modspec = 32 base::StringPrintf( 33 "configDir='%s' tokenDescription='Firefox NSS database' " 34 "flags=readOnly", 35 db_path.value().c_str()); 36 db_slot_ = SECMOD_OpenUserDB(modspec.c_str()); 37 return db_slot_ != NULL; 38 } 39 40 // This method is based on some NSS code in 41 // security/nss/lib/pk11wrap/pk11sdr.c, CVS revision 1.22 42 // This code is copied because the implementation assumes the use of the 43 // internal key slot for decryption, but we need to use another slot. 44 // The license block is: 45 /* ***** BEGIN LICENSE BLOCK ***** 46 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 47 * 48 * The contents of this file are subject to the Mozilla Public License Version 49 * 1.1 (the "License"); you may not use this file except in compliance with 50 * the License. You may obtain a copy of the License at 51 * http://www.mozilla.org/MPL/ 52 * 53 * Software distributed under the License is distributed on an "AS IS" basis, 54 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 55 * for the specific language governing rights and limitations under the 56 * License. 57 * 58 * The Original Code is the Netscape security libraries. 59 * 60 * The Initial Developer of the Original Code is 61 * Netscape Communications Corporation. 62 * Portions created by the Initial Developer are Copyright (C) 1994-2000 63 * the Initial Developer. All Rights Reserved. 64 * 65 * Contributor(s): 66 * thayes (at) netscape.com 67 * 68 * Alternatively, the contents of this file may be used under the terms of 69 * either the GNU General Public License Version 2 or later (the "GPL"), or 70 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 71 * in which case the provisions of the GPL or the LGPL are applicable instead 72 * of those above. If you wish to allow use of your version of this file only 73 * under the terms of either the GPL or the LGPL, and not to allow others to 74 * use your version of this file under the terms of the MPL, indicate your 75 * decision by deleting the provisions above and replace them with the notice 76 * and other provisions required by the GPL or the LGPL. If you do not delete 77 * the provisions above, a recipient may use your version of this file under 78 * the terms of any one of the MPL, the GPL or the LGPL. 79 * 80 * ***** END LICENSE BLOCK ***** */ 81 82 /* 83 * Data structure and template for encoding the result of an SDR operation 84 * This is temporary. It should include the algorithm ID of the encryption 85 * mechanism 86 */ 87 struct SDRResult 88 { 89 SECItem keyid; 90 SECAlgorithmID alg; 91 SECItem data; 92 }; 93 typedef struct SDRResult SDRResult; 94 95 static SEC_ASN1Template g_template[] = { 96 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, 97 { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, 98 { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), 99 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, 100 { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, 101 { 0 } 102 }; 103 104 static SECStatus 105 unpadBlock(SECItem *data, int blockSize, SECItem *result) 106 { 107 SECStatus rv = SECSuccess; 108 int padLength; 109 int i; 110 111 result->data = 0; 112 result->len = 0; 113 114 /* Remove the padding from the end if the input data */ 115 if (data->len == 0 || data->len % blockSize != 0) { 116 rv = SECFailure; 117 goto loser; 118 } 119 120 padLength = data->data[data->len-1]; 121 if (padLength > blockSize) { rv = SECFailure; goto loser; } 122 123 /* verify padding */ 124 for (i=data->len - padLength; static_cast<uint32>(i) < data->len; i++) { 125 if (data->data[i] != padLength) { 126 rv = SECFailure; 127 goto loser; 128 } 129 } 130 131 result->len = data->len - padLength; 132 result->data = (unsigned char *)PORT_Alloc(result->len); 133 if (!result->data) { rv = SECFailure; goto loser; } 134 135 PORT_Memcpy(result->data, data->data, result->len); 136 137 if (padLength < 2) { 138 return SECWouldBlock; 139 } 140 141 loser: 142 return rv; 143 } 144 145 /* decrypt a block */ 146 static SECStatus 147 pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, 148 CK_MECHANISM_TYPE type, PK11SymKey *key, 149 SECItem *params, SECItem *in, SECItem *result) 150 { 151 PK11Context *ctx = 0; 152 SECItem paddedResult; 153 SECStatus rv; 154 155 paddedResult.len = 0; 156 paddedResult.data = 0; 157 158 ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); 159 if (!ctx) { rv = SECFailure; goto loser; } 160 161 paddedResult.len = in->len; 162 paddedResult.data = static_cast<unsigned char*>( 163 PORT_ArenaAlloc(arena, paddedResult.len)); 164 165 rv = PK11_CipherOp(ctx, paddedResult.data, 166 (int*)&paddedResult.len, paddedResult.len, 167 in->data, in->len); 168 if (rv != SECSuccess) goto loser; 169 170 PK11_Finalize(ctx); 171 172 /* Remove the padding */ 173 rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); 174 if (rv) goto loser; 175 176 loser: 177 if (ctx) PK11_DestroyContext(ctx, PR_TRUE); 178 return rv; 179 } 180 181 SECStatus NSSDecryptor::PK11SDR_DecryptWithSlot( 182 PK11SlotInfo* slot, SECItem* data, SECItem* result, void* cx) const { 183 SECStatus rv = SECSuccess; 184 PK11SymKey *key = 0; 185 CK_MECHANISM_TYPE type; 186 SDRResult sdrResult; 187 SECItem *params = 0; 188 SECItem possibleResult = { siBuffer, NULL, 0 }; 189 PLArenaPool *arena = 0; 190 191 arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); 192 if (!arena) { rv = SECFailure; goto loser; } 193 194 /* Decode the incoming data */ 195 memset(&sdrResult, 0, sizeof sdrResult); 196 rv = SEC_QuickDERDecodeItem(arena, &sdrResult, g_template, data); 197 if (rv != SECSuccess) goto loser; /* Invalid format */ 198 199 /* Get the parameter values from the data */ 200 params = PK11_ParamFromAlgid(&sdrResult.alg); 201 if (!params) { rv = SECFailure; goto loser; } 202 203 /* Use triple-DES (Should look up the algorithm) */ 204 type = CKM_DES3_CBC; 205 key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); 206 if (!key) { 207 rv = SECFailure; 208 } else { 209 rv = pk11Decrypt(slot, arena, type, key, params, 210 &sdrResult.data, result); 211 } 212 213 /* 214 * if the pad value was too small (1 or 2), then it's statistically 215 * 'likely' that (1 in 256) that we may not have the correct key. 216 * Check the other keys for a better match. If we find none, use 217 * this result. 218 */ 219 if (rv == SECWouldBlock) 220 possibleResult = *result; 221 222 /* 223 * handle the case where your key indicies may have been broken 224 */ 225 if (rv != SECSuccess) { 226 PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); 227 PK11SymKey *testKey = NULL; 228 PK11SymKey *nextKey = NULL; 229 230 for (testKey = keyList; testKey; 231 testKey = PK11_GetNextSymKey(testKey)) { 232 rv = pk11Decrypt(slot, arena, type, testKey, params, 233 &sdrResult.data, result); 234 if (rv == SECSuccess) 235 break; 236 237 /* found a close match. If it's our first remember it */ 238 if (rv == SECWouldBlock) { 239 if (possibleResult.data) { 240 /* this is unlikely but possible. If we hit this condition, 241 * we have no way of knowing which possibility to prefer. 242 * in this case we just match the key the application 243 * thought was the right one */ 244 SECITEM_ZfreeItem(result, PR_FALSE); 245 } else { 246 possibleResult = *result; 247 } 248 } 249 } 250 251 /* free the list */ 252 for (testKey = keyList; testKey; testKey = nextKey) { 253 nextKey = PK11_GetNextSymKey(testKey); 254 PK11_FreeSymKey(testKey); 255 } 256 } 257 258 /* we didn't find a better key, use the one with a small pad value */ 259 if ((rv != SECSuccess) && (possibleResult.data)) { 260 *result = possibleResult; 261 possibleResult.data = NULL; 262 rv = SECSuccess; 263 } 264 265 loser: 266 if (arena) PORT_FreeArena(arena, PR_TRUE); 267 if (key) PK11_FreeSymKey(key); 268 if (params) SECITEM_ZfreeItem(params, PR_TRUE); 269 if (possibleResult.data) SECITEM_ZfreeItem(&possibleResult, PR_FALSE); 270 271 return rv; 272 } 273