1/* 2 * Copyright (c) 2003,2005 Apple Computer, Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please 7 * obtain a copy of the License at http://www.apple.com/publicsource and 8 * read it before using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 15 * Please see the License for the specific language governing rights and 16 * limitations under the License. 17 */ 18 19/* 20 * p12Crypto.cpp - PKCS12 Crypto routines. App space reference version. 21 * 22 * Created 2/28/03 by Doug Mitchell. 23 */ 24 25#include "p12Crypto.h" 26#include "pkcs12Utils.h" 27#include <security_cdsa_utils/cuCdsaUtils.h> 28#include <Security/cssmapple.h> 29 30/* 31 * Free memory via specified plugin's app-level allocator 32 */ 33static void appFreeCssmMemory( 34 CSSM_HANDLE hand, 35 void *p) 36{ 37 CSSM_API_MEMORY_FUNCS memFuncs; 38 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); 39 if(crtn) { 40 cssmPerror("CSSM_GetAPIMemoryFunctions", crtn); 41 /* oh well, leak and continue */ 42 return; 43 } 44 memFuncs.free_func(p, memFuncs.AllocRef); 45} 46 47#define KEY_LABEL "p12 key" 48 49/* 50 * Given appropriate P12-style parameters, cook up a CSSM_KEY. 51 */ 52CSSM_RETURN p12KeyGen_app( 53 CSSM_CSP_HANDLE cspHand, 54 CSSM_KEY &key, 55 bool isForEncr, // true: en/decrypt false: MAC 56 CSSM_ALGORITHMS keyAlg, 57 CSSM_ALGORITHMS pbeHashAlg, // SHA1, MD5 only 58 uint32 keySizeInBits, 59 uint32 iterCount, 60 const CSSM_DATA &salt, 61 const CSSM_DATA &pwd, // unicode, double null terminated 62 CSSM_DATA &iv) // referent is optional 63{ 64 CSSM_RETURN crtn; 65 CSSM_CC_HANDLE ccHand; 66 CSSM_DATA dummyLabel; 67 CSSM_ACCESS_CREDENTIALS creds; 68 69 memset(&key, 0, sizeof(CSSM_KEY)); 70 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 71 72 /* infer key derivation algorithm */ 73 CSSM_ALGORITHMS deriveAlg = CSSM_ALGID_NONE; 74 if(pbeHashAlg != CSSM_ALGID_SHA1) { 75 return CSSMERR_CSP_INVALID_ALGORITHM; 76 } 77 if(isForEncr) { 78 /* 79 * FIXME - if this key is going to be used to wrap/unwrap a 80 * shrouded key bag, its usage will change accordingly... 81 */ 82 deriveAlg = CSSM_ALGID_PKCS12_PBE_ENCR; 83 } 84 else { 85 deriveAlg = CSSM_ALGID_PKCS12_PBE_MAC; 86 } 87 CSSM_CRYPTO_DATA seed; 88 seed.Param = pwd; 89 seed.Callback = NULL; 90 seed.CallerCtx = NULL; 91 92 crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, 93 deriveAlg, 94 keyAlg, 95 keySizeInBits, 96 &creds, 97 NULL, // BaseKey 98 iterCount, 99 &salt, 100 &seed, // seed 101 &ccHand); 102 if(crtn) { 103 cuPrintError("CSSM_CSP_CreateDeriveKeyContext", crtn); 104 return crtn; 105 } 106 107 dummyLabel.Length = strlen(KEY_LABEL); 108 dummyLabel.Data = (uint8 *)KEY_LABEL; 109 110 /* KEYUSE_ANY - this is just an ephemeral session key */ 111 crtn = CSSM_DeriveKey(ccHand, 112 &iv, 113 CSSM_KEYUSE_ANY, 114 //CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, 115 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, 116 &dummyLabel, 117 NULL, // cred and acl 118 &key); 119 CSSM_DeleteContext(ccHand); 120 if(crtn) { 121 cuPrintError("CSSM_DeriveKey", crtn); 122 } 123 return crtn; 124} 125 126/* 127 * Decrypt (typically, an encrypted P7 ContentInfo contents or 128 * a P12 ShroudedKeyBag). 129 */ 130CSSM_RETURN p12Decrypt_app( 131 CSSM_CSP_HANDLE cspHand, 132 const CSSM_DATA &cipherText, 133 CSSM_ALGORITHMS keyAlg, 134 CSSM_ALGORITHMS encrAlg, 135 CSSM_ALGORITHMS pbeHashAlg, // SHA1, MD5 only 136 uint32 keySizeInBits, 137 uint32 blockSizeInBytes, // for IV 138 CSSM_PADDING padding, // CSSM_PADDING_PKCS7, etc. 139 CSSM_ENCRYPT_MODE mode, // CSSM_ALGMODE_CBCPadIV8, etc. 140 uint32 iterCount, 141 const CSSM_DATA &salt, 142 const CSSM_DATA &pwd, // unicode, double null terminated 143 SecNssCoder &coder, // for mallocing KeyData and plainText 144 CSSM_DATA &plainText) 145{ 146 CSSM_RETURN crtn; 147 CSSM_KEY ckey; 148 CSSM_CC_HANDLE ccHand = 0; 149 150 /* P12 style IV derivation, optional */ 151 CSSM_DATA iv = {0, NULL}; 152 CSSM_DATA_PTR ivPtr = NULL; 153 if(blockSizeInBytes) { 154 coder.allocItem(iv, blockSizeInBytes); 155 ivPtr = &iv; 156 } 157 158 /* P12 style key derivation */ 159 crtn = p12KeyGen_app(cspHand, ckey, true, keyAlg, pbeHashAlg, 160 keySizeInBits, iterCount, salt, pwd, iv); 161 if(crtn) { 162 return crtn; 163 } 164 165 /* CSSM context */ 166 crtn = CSSM_CSP_CreateSymmetricContext(cspHand, 167 encrAlg, 168 mode, 169 NULL, // access cred 170 &ckey, 171 ivPtr, // InitVector, optional 172 padding, 173 NULL, // Params 174 &ccHand); 175 if(crtn) { 176 cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn); 177 return crtn; 178 } 179 180 /* go - CSP mallocs ptext and rem data */ 181 CSSM_DATA ourPtext = {0, NULL}; 182 CSSM_DATA remData = {0, NULL}; 183 uint32 bytesDecrypted; 184 crtn = CSSM_DecryptData(ccHand, 185 &cipherText, 186 1, 187 &ourPtext, 188 1, 189 &bytesDecrypted, 190 &remData); 191 if(crtn) { 192 cuPrintError("CSSM_EncryptData", crtn); 193 } 194 else { 195 coder.allocCopyItem(ourPtext, plainText); 196 plainText.Length = bytesDecrypted; 197 198 /* plaintext copied into coder space; free the memory allocated 199 * by the CSP */ 200 appFreeCssmMemory(cspHand, ourPtext.Data); 201 } 202 /* an artifact of CSPFUllPLuginSession - this never contains 203 * valid data but sometimes gets mallocds */ 204 if(remData.Data) { 205 appFreeCssmMemory(cspHand, remData.Data); 206 } 207 CSSM_DeleteContext(ccHand); 208 CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE); 209 return crtn; 210} 211 212/* 213 * Calculate the MAC for a PFX. Caller is either going compare 214 * the result against an existing PFX's MAC or drop the result into 215 * a newly created PFX. 216 */ 217CSSM_RETURN p12GenMac_app( 218 CSSM_CSP_HANDLE cspHand, 219 const CSSM_DATA &ptext, // e.g., NSS_P12_DecodedPFX.derAuthSaafe 220 CSSM_ALGORITHMS alg, // better be SHA1! 221 unsigned iterCount, 222 const CSSM_DATA &salt, 223 const CSSM_DATA &pwd, // unicode, double null terminated 224 SecNssCoder &coder, // for mallocing macData 225 CSSM_DATA &macData) // RETURNED 226{ 227 CSSM_RETURN crtn; 228 229 /* P12 style key derivation */ 230 unsigned keySizeInBits; 231 CSSM_ALGORITHMS hmacAlg; 232 switch(alg) { 233 case CSSM_ALGID_SHA1: 234 keySizeInBits = 160; 235 hmacAlg = CSSM_ALGID_SHA1HMAC; 236 break; 237 case CSSM_ALGID_MD5: 238 /* not even sure if this is legal in p12 world... */ 239 keySizeInBits = 128; 240 hmacAlg = CSSM_ALGID_MD5HMAC; 241 break; 242 default: 243 return CSSMERR_CSP_INVALID_ALGORITHM; 244 } 245 CSSM_KEY macKey; 246 CSSM_DATA iv = {0, NULL}; 247 crtn = p12KeyGen_app(cspHand, macKey, false, hmacAlg, alg, 248 keySizeInBits, iterCount, salt, pwd, iv); 249 if(crtn) { 250 return crtn; 251 } 252 253 /* prealloc the mac data */ 254 coder.allocItem(macData, keySizeInBits / 8); 255 CSSM_CC_HANDLE ccHand = 0; 256 crtn = CSSM_CSP_CreateMacContext(cspHand, hmacAlg, &macKey, &ccHand); 257 if(crtn) { 258 cuPrintError("CSSM_CSP_CreateMacContext", crtn); 259 return crtn; 260 } 261 262 crtn = CSSM_GenerateMac (ccHand, &ptext, 1, &macData); 263 if(crtn) { 264 cuPrintError("CSSM_GenerateMac", crtn); 265 } 266 CSSM_DeleteContext(ccHand); 267 CSSM_FreeKey(cspHand, NULL, &macKey, CSSM_FALSE); 268 return crtn; 269} 270 271/* 272 * Verify MAC on an existing PFX. 273 */ 274CSSM_RETURN p12VerifyMac_app( 275 const NSS_P12_DecodedPFX &pfx, 276 CSSM_CSP_HANDLE cspHand, 277 const CSSM_DATA &pwd, // unicode, double null terminated 278 SecNssCoder &coder) // for temp mallocs 279{ 280 if(pfx.macData == NULL) { 281 return CSSMERR_CSP_INVALID_SIGNATURE; 282 } 283 NSS_P12_MacData &macData = *pfx.macData; 284 NSS_P7_DigestInfo &digestInfo = macData.mac; 285 CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm; 286 CSSM_ALGORITHMS macAlg; 287 if(!cssmOidToAlg(&algOid, &macAlg)) { 288 return CSSMERR_CSP_INVALID_ALGORITHM; 289 } 290 uint32 iterCount = 0; 291 CSSM_DATA &citer = macData.iterations; 292 if(!p12DataToInt(citer, iterCount)) { 293 return CSSMERR_CSP_INVALID_ATTR_ROUNDS; 294 } 295 if(iterCount == 0) { 296 /* optional, default 1 */ 297 iterCount = 1; 298 } 299 300 /* 301 * In classic fashion, the PKCS12 spec now says: 302 * 303 * When password integrity mode is used to secure a PFX PDU, 304 * an SHA-1 HMAC is computed on the BER-encoding of the contents 305 * of the content field of the authSafe field in the PFX PDU. 306 * 307 * So here we go. 308 */ 309 CSSM_DATA genMac; 310 CSSM_RETURN crtn = p12GenMac_app(cspHand, *pfx.authSafe.content.data, 311 macAlg, iterCount, macData.macSalt, pwd, coder, genMac); 312 if(crtn) { 313 return crtn; 314 } 315 if(nssCompareCssmData(&genMac, &digestInfo.digest)) { 316 return CSSM_OK; 317 } 318 else { 319 return CSSMERR_CSP_VERIFY_FAILED; 320 } 321} 322 323