1/* 2 * Copyright (c) 2000-2001,2011-2012,2014 Apple 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 obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * 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 EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19// 20// wrapKeyCms.cpp - wrap/unwrap key, CMS format 21// 22 23#include "AppleCSPSession.h" 24#include "AppleCSPUtils.h" 25#include "AppleCSPKeys.h" 26#include "cspdebugging.h" 27 28/* 29 * 30 * Here is the algorithm implemented in this module: 31 * 32 * Note that DEK is the wrapping key, 33 * 34 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the 35 * following concatenation: 36 * 37 * 4-byte length of Descriptive Data, big-endian | 38 * Descriptive Data | 39 * rawBlob.Data bytes 40 * 41 * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with 42 * PKCS1 padding. Call the ciphertext TEMP1 43 * 44 * 3. Let TEMP2 = IV || TEMP1. 45 * 46 * 4. Reverse the order of the octets in TEMP2 call the result TEMP3. 47 * 48 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode 49 * with PKCS1 padding call the result TEMP4. 50 * 51 * TEMP4 is wrappedKey.KeyData. 52 */ 53 54/* true: cook up second CCHandle via a new HandleObject 55 * false - OK to reuse a CCHandle */ 56#define USE_SECOND_CCHAND 0 57 58/* false : make copy of incoming context before changing IV 59 * true : resuse OK */ 60#define REUSE_CONTEXT 1 61 62/* lots'o'printfs in lieu of a debugger which works */ 63#define VERBOSE_DEBUG 0 64 65static const uint8 magicCmsIv[] = 66 { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 }; 67 68#if VERBOSE_DEBUG 69static void dumpBuf( 70 char *title, 71 const CSSM_DATA *d, 72 uint32 maxLen) 73{ 74 unsigned i; 75 uint32 len; 76 77 if(title) { 78 printf("%s: ", title); 79 } 80 if(d == NULL) { 81 printf("NO DATA\n"); 82 return; 83 } 84 printf("Total Length: %d\n ", d->Length); 85 len = maxLen; 86 if(d->Length < len) { 87 len = d->Length; 88 } 89 for(i=0; i<len; i++) { 90 printf("%02X ", d->Data[i]); 91 if((i % 16) == 15) { 92 printf("\n "); 93 } 94 } 95 printf("\n"); 96} 97#else 98#define dumpBuf(t, d, m) 99#endif /* VERBOSE_DEBUG */ 100 101 102/* serialize/deserialize uint32, big-endian. */ 103static void serializeUint32(uint32 i, uint8 *buf) 104{ 105 *buf++ = (uint8)(i >> 24); 106 *buf++ = (uint8)(i >> 16); 107 *buf++ = (uint8)(i >> 8); 108 *buf = (uint8)i; 109} 110 111static uint32 deserializeUint32(const uint8 *buf) { 112 uint32 result; 113 114 result = ((uint32)buf[0] << 24) | 115 ((uint32)buf[1] << 16) | 116 ((uint32)buf[2] << 8) | 117 (uint32)buf[3]; 118 return result; 119} 120 121void AppleCSPSession::WrapKeyCms( 122 CSSM_CC_HANDLE CCHandle, 123 const Context &context, 124 const AccessCredentials &AccessCred, 125 const CssmKey &UnwrappedKey, 126 CssmData &rawBlob, 127 bool allocdRawBlob, // callee has to free rawBlob 128 const CssmData *DescriptiveData, 129 CssmKey &WrappedKey, 130 CSSM_PRIVILEGE Privilege) 131{ 132 uint32 ddLen; 133 CssmData PRIVATE_KEY_BYTES; 134 #if !REUSE_CONTEXT 135 Context secondCtx(context.ContextType, context.AlgorithmType); 136 secondCtx.copyFrom(context, privAllocator); 137 #endif /* REUSE_CONTEXT */ 138 139 /* 140 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the 141 * following concatenation: 142 * 143 * 4-byte length of Descriptive Data, big-endian | 144 * Descriptive Data | 145 * rawBlob.Data bytes 146 */ 147 dumpBuf("wrap rawBlob", &rawBlob, 24); 148 dumpBuf("wrap DescriptiveData", DescriptiveData, 24); 149 150 if(DescriptiveData == NULL) { 151 ddLen = 0; 152 } 153 else { 154 ddLen = (uint32) DescriptiveData->Length; 155 } 156 size_t pkbLen = 4 + ddLen + rawBlob.Length; 157 setUpCssmData(PRIVATE_KEY_BYTES, pkbLen, privAllocator); 158 uint8 *cp = PRIVATE_KEY_BYTES.Data; 159 serializeUint32(ddLen, cp); 160 cp += 4; 161 if(ddLen != 0) { 162 memcpy(cp, DescriptiveData->Data, ddLen); 163 cp += ddLen; 164 } 165 memcpy(cp, rawBlob.Data, rawBlob.Length); 166 dumpBuf("wrap PRIVATE_KEY_BYTES", &PRIVATE_KEY_BYTES, 48); 167 168 /* 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with 169 * PKCS1 padding. Call the ciphertext TEMP1 170 * 171 * We'll just use the caller's context for this. Maybe we should 172 * validate mode, padding, IV? 173 */ 174 CssmData TEMP1; 175 CSSM_SIZE bytesEncrypted; 176 CssmData remData; 177 EncryptData(CCHandle, 178 context, 179 &PRIVATE_KEY_BYTES, // ClearBufs[] 180 1, // ClearBufCount 181 &TEMP1, // CipherBufs[], 182 1, // CipherBufCount, 183 bytesEncrypted, 184 remData, 185 Privilege); 186 187 // I'm not 100% sure about this.... 188 assert(remData.Length == 0); 189 TEMP1.Length = bytesEncrypted; 190 dumpBuf("wrap TEMP1", &TEMP1, 48); 191 192 /* 193 * 3. Let TEMP2 = IV || TEMP1. 194 */ 195 CssmData TEMP2; 196 CssmData &IV = context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR, 197 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); 198 setUpCssmData(TEMP2, IV.Length + TEMP1.Length, privAllocator); 199 memcpy(TEMP2.Data, IV.Data, IV.Length); 200 memcpy(TEMP2.Data + IV.Length, TEMP1.Data, TEMP1.Length); 201 dumpBuf("wrap TEMP2", &TEMP2, 56); 202 203 204 /* 205 * 4. Reverse the order of the octets in TEMP2 call the result 206 * TEMP3. 207 */ 208 CssmData TEMP3; 209 setUpCssmData(TEMP3, TEMP2.Length, privAllocator); 210 uint8 *cp2 = TEMP2.Data + TEMP2.Length - 1; 211 cp = TEMP3.Data; 212 for(uint32 i=0; i<TEMP2.Length; i++) { 213 *cp++ = *cp2--; 214 } 215 dumpBuf("wrap TEMP3", &TEMP3, 64); 216 217 /* 218 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode 219 * with PKCS1 padding call the result TEMP4. 220 * 221 * TEMP4 is wrappedKey.KeyData. 222 * 223 * This is the tricky part - we're going to use the caller's context 224 * again, but we're going to modify the IV. 225 * We're assuming here that the IV we got via context.get<CssmData> 226 * actually is in the context and not a copy! 227 */ 228 #if REUSE_CONTEXT 229 CssmData &IV2 = context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR, 230 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); 231 #else 232 CssmData &IV2 = secondCtx.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR, 233 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); 234 #endif /* REUSE_CONTEXT */ 235 236 uint8 *savedIV = IV2.Data; 237 CSSM_SIZE savedIVLen = IV2.Length; 238 IV2.Data = (uint8 *)magicCmsIv; 239 IV2.Length = 8; 240 CssmData &outBlob = CssmData::overlay(WrappedKey.KeyData); 241 outBlob.Length = 0; 242 outBlob.Data = NULL; 243 try { 244 EncryptData(CCHandle, 245 #if REUSE_CONTEXT 246 context, 247 #else 248 secondCtx, 249 #endif /* REUSE_CONTEXT */ 250 251 &TEMP3, // ClearBufs[] 252 1, // ClearBufCount 253 &outBlob, // CipherBufs[], 254 1, // CipherBufCount, 255 bytesEncrypted, 256 remData, 257 Privilege); 258 } 259 catch (...) { 260 IV2.Data = savedIV; 261 IV2.Length = savedIVLen; 262 throw; // and leak 263 } 264 IV2.Data = savedIV; 265 IV2.Length = savedIVLen; 266 267 // I'm not 100% sure about this.... 268 assert(remData.Length == 0); 269 outBlob.Length = bytesEncrypted; 270 dumpBuf("wrap outBlob", &outBlob, 64); 271 272 /* outgoing header */ 273 WrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED; 274 // OK to be zero or not present 275 WrappedKey.KeyHeader.WrapMode = context.getInt(CSSM_ATTRIBUTE_MODE); 276 WrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM; 277 278 /* free resources */ 279 freeCssmData(PRIVATE_KEY_BYTES, privAllocator); 280 freeCssmData(TEMP1, normAllocator); // alloc via encrypt 281 freeCssmData(TEMP2, privAllocator); 282 freeCssmData(TEMP3, privAllocator); 283 if(allocdRawBlob) { 284 /* our caller mallocd this when dereferencing a ref key */ 285 freeCssmData(rawBlob, privAllocator); 286 } 287} 288 289/* note we expect an IV present in the context though we don't use it 290 * FIXME - we should figure out how to add this attribute at this level 291 */ 292 293/* safety trap - don't try to malloc anything bigger than this - we get 294 * sizes from the processed bit stream.... */ 295#define MAX_MALLOC_SIZE 0x10000 296 297void AppleCSPSession::UnwrapKeyCms( 298 CSSM_CC_HANDLE CCHandle, 299 const Context &Context, 300 const CssmKey &WrappedKey, 301 const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry, 302 CssmKey &UnwrappedKey, 303 CssmData &DescriptiveData, 304 CSSM_PRIVILEGE Privilege, 305 cspKeyStorage keyStorage) 306{ 307 /* 308 * In reverse order, the steps from wrap... 309 * 310 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode 311 * with PKCS1 padding call the result TEMP4. 312 * 313 * TEMP4 is wrappedKey.KeyData. 314 */ 315 const CssmData &wrappedBlob = CssmData::overlay(WrappedKey.KeyData); 316 dumpBuf("unwrap inBlob", &wrappedBlob, 64); 317 CssmData &IV1 = Context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR, 318 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR); 319 uint8 *savedIV = IV1.Data; 320 CSSM_SIZE savedIvLen = IV1.Length; 321 IV1.Data = (uint8 *)magicCmsIv; 322 IV1.Length = 8; 323 CssmData TEMP3; 324 CSSM_SIZE bytesDecrypted; 325 CssmData remData; 326 327 try { 328 DecryptData(CCHandle, 329 Context, 330 &wrappedBlob, // CipherBufs[], 331 1, // CipherBufCount, 332 &TEMP3, // ClearBufs[] 333 1, // ClearBufCount 334 bytesDecrypted, 335 remData, 336 Privilege); 337 } 338 catch(...) { 339 IV1.Data = savedIV; 340 IV1.Length = savedIvLen; 341 throw; 342 } 343 IV1.Data = savedIV; 344 IV1.Length = savedIvLen; 345 // I'm not 100% sure about this.... 346 assert(remData.Length == 0); 347 TEMP3.Length = bytesDecrypted; 348 dumpBuf("unwrap TEMP3", &TEMP3, 64); 349 350 /* 351 * 4. Reverse the order of the octets in TEMP2 call the result 352 * TEMP3. 353 * 354 * i.e., TEMP2 := reverse(TEMP3) 355 */ 356 CssmData TEMP2; 357 setUpCssmData(TEMP2, TEMP3.Length, privAllocator); 358 uint8 *src = TEMP3.Data + TEMP3.Length - 1; 359 uint8 *dst = TEMP2.Data; 360 for(uint32 i=0; i<TEMP2.Length; i++) { 361 *dst++ = *src--; 362 } 363 dumpBuf("unwrap TEMP2", &TEMP2, 64); 364 365 /* 366 * 3. Let TEMP2 = IV || TEMP1. 367 * 368 * IV2 is first 8 bytes of TEMP2, remainder is TEMP1 369 */ 370 if(TEMP2.Length <= 8) { 371 dprintf0("UnwrapKeyCms: short TEMP2\n"); 372 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); 373 } 374 CssmData IV2; 375 CssmData TEMP1; 376 setUpCssmData(IV2, 8, privAllocator); 377 setUpCssmData(TEMP1, TEMP2.Length - 8, privAllocator); 378 memcpy(IV2.Data, TEMP2.Data, 8); 379 memcpy(TEMP1.Data, TEMP2.Data + 8, TEMP1.Length); 380 dumpBuf("unwrap TEMP1", &TEMP1, 48); 381 382 /* 383 * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with 384 * PKCS1 padding. Call the ciphertext TEMP1 385 * 386 * i.e., decrypt TEMP1 to get PRIVATE_KEY_BYTES. Use IV2, not caller's 387 * IV. We already saved caller's IV in savediV and savedIvLen. 388 */ 389 IV1 = IV2; 390 CssmData PRIVATE_KEY_BYTES; 391 try { 392 DecryptData(CCHandle, 393 Context, 394 &TEMP1, // CipherBufs[], 395 1, // CipherBufCount, 396 &PRIVATE_KEY_BYTES, // ClearBufs[] 397 1, // ClearBufCount 398 bytesDecrypted, 399 remData, 400 Privilege); 401 } 402 catch(...) { 403 IV1.Data = savedIV; 404 IV1.Length = savedIvLen; 405 throw; 406 } 407 IV1.Data = savedIV; 408 // I'm not 100% sure about this.... 409 assert(remData.Length == 0); 410 PRIVATE_KEY_BYTES.Length = bytesDecrypted; 411 dumpBuf("unwrap PRIVATE_KEY_BYTES", &PRIVATE_KEY_BYTES, 64); 412 413 /* 414 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the 415 * following concatenation: 416 * 417 * 4-byte length of Descriptive Data, big-endian | 418 * Descriptive Data | 419 * rawBlob.Data bytes 420 */ 421 if(PRIVATE_KEY_BYTES.Length < 4) { 422 dprintf0("UnwrapKeyCms: short PRIVATE_KEY_BYTES\n"); 423 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); 424 } 425 uint8 *cp1 = PRIVATE_KEY_BYTES.Data; 426 uint32 ddLen = deserializeUint32(cp1); 427 cp1 += 4; 428 if(ddLen > MAX_MALLOC_SIZE) { 429 dprintf0("UnwrapKeyCms: preposterous ddLen in PRIVATE_KEY_BYTES\n"); 430 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); 431 } 432 setUpCssmData(DescriptiveData, ddLen, normAllocator); 433 memcpy(DescriptiveData.Data, cp1, ddLen); 434 cp1 += ddLen; 435 size_t outBlobLen = PRIVATE_KEY_BYTES.Length - ddLen - 4; 436 if(ddLen > MAX_MALLOC_SIZE) { 437 dprintf0("UnwrapKeyCms: preposterous outBlobLen in PRIVATE_KEY_BYTES\n"); 438 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); 439 } 440 CssmData &outBlob = CssmData::overlay(UnwrappedKey.KeyData); 441 setUpCssmData(outBlob, outBlobLen, normAllocator); 442 memcpy(outBlob.Data, cp1, outBlobLen); 443 444 /* set up outgoing header */ 445 UnwrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; 446 UnwrappedKey.KeyHeader.Format = inferFormat(UnwrappedKey); 447 448 /* 449 * Cook up a BinaryKey if caller wants a reference key. 450 */ 451 if(keyStorage == CKS_Ref) { 452 BinaryKey *binKey = NULL; 453 CSPKeyInfoProvider *provider = infoProvider(UnwrappedKey); 454 /* optional parameter-bearing key */ 455 CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY); 456 provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey); 457 addRefKey(*binKey, UnwrappedKey); 458 delete provider; 459 } 460 /* free resources */ 461 freeCssmData(PRIVATE_KEY_BYTES, normAllocator); // alloc via decrypt 462 freeCssmData(TEMP1, privAllocator); 463 freeCssmData(IV2, privAllocator); 464 freeCssmData(TEMP2, privAllocator); 465 freeCssmData(TEMP3, normAllocator); // via decrypt 466 467} 468 469