1/* 2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25#include "SecOTR.h" 26#include "SecOTRIdentityPriv.h" 27#include <utilities/array_size.h> 28#include <utilities/SecCFWrappers.h> 29 30#include <AssertMacros.h> 31 32#include <CoreFoundation/CFNumber.h> 33#include <CoreFoundation/CFString.h> 34#include <CoreFoundation/CFData.h> 35 36#include <Security/SecItem.h> 37#include <Security/SecKeyPriv.h> 38 39#include <Security/oidsalg.h> 40#include <Security/SecCertificatePriv.h> 41 42#include "SecOTRErrors.h" 43 44#include <TargetConditionals.h> 45 46// 47// Algorthim ID initialization 48// 49 50#define kMessageIdentityRSAKeyBits 1280 51#define kMessageIdentityECKeyBits 256 52 53void EnsureOTRAlgIDInited(void) 54{ 55 static dispatch_once_t kSignatureAlgID_ONCE; 56 static SecAsn1AlgId kOTRECSignatureAlgID; 57 58 dispatch_once(&kSignatureAlgID_ONCE, ^{ 59 kOTRECSignatureAlgID.algorithm = CSSMOID_ECDSA_WithSHA1; 60 kOTRSignatureAlgIDPtr = &kOTRECSignatureAlgID; 61 }); 62} 63 64 65static CFStringRef sSigningKeyName = CFSTR("OTR Signing Key"); 66 67// 68// SecOTRFullIdentity implementation 69// 70 71CFGiblisFor(SecOTRFullIdentity); 72 73static CF_RETURNS_RETAINED CFStringRef SecOTRFullIdentityCopyDescription(CFTypeRef cf) { 74 SecOTRFullIdentityRef requestor = (SecOTRFullIdentityRef)cf; 75 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicIdentity: %p %02x%02x%02x%02x%02x%02x%02x%02x>"), 76 requestor, 77 requestor->publicIDHash[0], requestor->publicIDHash[1], 78 requestor->publicIDHash[2], requestor->publicIDHash[3], 79 requestor->publicIDHash[4], requestor->publicIDHash[5], 80 requestor->publicIDHash[6], requestor->publicIDHash[7]); 81} 82 83static void SecOTRFullIdentityDestroy(CFTypeRef cf) { 84 SecOTRFullIdentityRef requestor = (SecOTRFullIdentityRef)cf; 85 86 CFReleaseNull(requestor->privateSigningKey); 87 CFReleaseNull(requestor->publicSigningKey); 88} 89 90 91// 92// Shared statics 93// 94 95static OSStatus SecOTRFIPurgeFromKeychainByValue(SecKeyRef key, CFStringRef label) 96{ 97 OSStatus status; 98 const void *keys[] = { kSecClass, 99 kSecAttrLabel, 100 kSecValueRef, 101 }; 102 const void *values[] = { kSecClassKey, 103 label, 104 key, 105 }; 106 CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, array_size(values), NULL, NULL); 107 status = SecItemDelete(dict); 108 CFReleaseSafe(dict); 109 110 return status; 111} 112 113static bool SecKeyDigestAndSignWithError( 114 SecKeyRef key, /* Private key */ 115 const SecAsn1AlgId *algId, /* algorithm oid/params */ 116 const uint8_t *dataToDigest, /* signature over this data */ 117 size_t dataToDigestLen,/* length of dataToDigest */ 118 uint8_t *sig, /* signature, RETURNED */ 119 size_t *sigLen, /* IN/OUT */ 120 CFErrorRef *error) { 121 122 OSStatus status = SecKeyDigestAndSign(key, algId, dataToDigest, dataToDigestLen, sig, sigLen); 123 require_noerr(status, fail); 124 return true; 125fail: 126 SecOTRCreateError(secOTRErrorOSError, status, CFSTR("Error signing message. OSStatus in error code."), NULL, error); 127 return false; 128} 129 130// 131// SecOTRFullIdentity Functions 132// 133 134static bool SecOTRFICachePublicHash(SecOTRFullIdentityRef fullID, CFErrorRef *error) 135{ 136 SecOTRPublicIdentityRef pubID = SecOTRPublicIdentityCopyFromPrivate(NULL, fullID, error); 137 138 require(pubID, fail); 139 140 SecOTRPICopyHash(pubID, fullID->publicIDHash); 141 142fail: 143 CFReleaseSafe(pubID); 144 return (pubID != NULL); // This is safe because we're not accessing the value after release, just checking if it ever had a value of some nature. 145} 146 147#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR 148#define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v)); 149SEC_CONST_DECL (kSecAttrAccessible, "pdmn"); 150SEC_CONST_DECL (kSecAttrAccessibleAlwaysThisDeviceOnly, "dku"); 151#endif 152 153SecOTRFullIdentityRef SecOTRFullIdentityCreate(CFAllocatorRef allocator, CFErrorRef *error) 154{ 155 CFDictionaryRef keygen_parameters = NULL; 156 SecOTRFullIdentityRef newID = CFTypeAllocate(SecOTRFullIdentity, struct _SecOTRFullIdentity, allocator); 157 SecKeyRef tempSigningKey = NULL; 158 159 newID->publicSigningKey = NULL; 160 newID->privateSigningKey = NULL; 161 162 require(newID, out); 163 164 EnsureOTRAlgIDInited(); 165 166 const int signing_keySizeLocal = kMessageIdentityECKeyBits; 167 CFNumberRef signing_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &signing_keySizeLocal); 168 169 const void *signing_keygen_keys[] = { kSecAttrKeyType, 170 kSecAttrKeySizeInBits, 171 kSecAttrIsPermanent, 172 kSecAttrAccessible, 173 kSecAttrLabel, 174 }; 175 176 const void *signing_keygen_vals[] = { kSecAttrKeyTypeEC, 177 signing_bitsize, 178 kCFBooleanTrue, 179 kSecAttrAccessibleAlwaysThisDeviceOnly, 180 sSigningKeyName 181 }; 182 keygen_parameters = CFDictionaryCreate(kCFAllocatorDefault, 183 signing_keygen_keys, signing_keygen_vals, array_size(signing_keygen_vals), 184 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); 185 CFReleaseNull(signing_bitsize); 186 require_noerr(SecKeyGeneratePair(keygen_parameters, &tempSigningKey, &newID->privateSigningKey), out); 187 CFReleaseNull(keygen_parameters); 188 189 newID->publicSigningKey = SecKeyCreatePublicFromPrivate(tempSigningKey); 190 191 (void) SecOTRFIPurgeFromKeychainByValue(tempSigningKey, sSigningKeyName); 192 CFReleaseNull(tempSigningKey); 193 194 require(SecOTRFICachePublicHash(newID, error), out); 195 196 return newID; 197 198out: 199 if (NULL != newID) { 200 SecOTRFIPurgeFromKeychain(newID, NULL); 201 } 202 CFReleaseSafe(keygen_parameters); 203 CFReleaseSafe(newID); 204 CFReleaseSafe(tempSigningKey); 205 return NULL; 206} 207 208 209static 210OSStatus SecOTRFICreatePrivateKeyReadPersistentRef(const uint8_t **bytes, size_t *size, SecKeyRef* privateKey) 211{ 212 OSStatus status = errSecParam; 213 uint16_t dataSize; 214 CFDataRef persistentRef = NULL; 215 216 require_noerr_quiet(readSize(bytes, size, &dataSize), fail); 217 require_quiet(dataSize <= *size, fail); 218 219 SecKeyRef lookedUpKey = NULL; 220 persistentRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, *bytes, dataSize, kCFAllocatorNull); 221 require_quiet(persistentRef, fail); 222 223 require_noerr_quiet(SecKeyFindWithPersistentRef(persistentRef, &lookedUpKey), fail); 224 225 *privateKey = lookedUpKey; 226 227 *bytes += dataSize; 228 *size -= dataSize; 229 230 status = errSecSuccess; 231 232fail: 233 CFReleaseSafe(persistentRef); 234 235 return status; 236} 237 238static 239OSStatus SecOTRFICreateKeysFromReadPersistentRef(const uint8_t **bytes, size_t *size, SecKeyRef *publicKey, SecKeyRef* privateKey) 240{ 241 SecKeyRef foundKey = NULL; 242 243 OSStatus status = SecOTRFICreatePrivateKeyReadPersistentRef(bytes, size, &foundKey); 244 require_noerr_quiet(status, fail); 245 require_quiet(foundKey, fail); 246 247 *publicKey = SecKeyCreatePublicFromPrivate(*privateKey); 248 require_action_quiet(*publicKey, fail, status = errSecParam); 249 250 *privateKey = foundKey; 251 foundKey = NULL; 252 253 status = errSecSuccess; 254 255fail: 256 CFReleaseSafe(foundKey); 257 return status; 258} 259 260typedef SecKeyRef (*SecOTRPublicKeyCreateFunction)(CFAllocatorRef allocator, const uint8_t** data, size_t* limit); 261 262static 263OSStatus SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(const uint8_t **bytes, size_t *size, SecKeyRef *publicKey, SecKeyRef* privateKey, SecOTRPublicKeyCreateFunction createPublic) 264{ 265 SecKeyRef foundKey = NULL; 266 267 OSStatus status = SecOTRFICreatePrivateKeyReadPersistentRef(bytes, size, &foundKey); 268 require_noerr_quiet(status, fail); 269 require_quiet(foundKey, fail); 270 271 *publicKey = (*createPublic)(NULL, bytes, size); 272 require_action_quiet(*publicKey, fail, status = errSecParam); 273 274 *privateKey = foundKey; 275 276fail: 277 return status; 278} 279 280static 281OSStatus SecOTRFIInitFromV1Bytes(SecOTRFullIdentityRef newID, CFAllocatorRef allocator, 282 const uint8_t **bytes,size_t *size) { 283 require(**bytes == 1, fail); 284 ++*bytes; 285 --*size; 286 287 require_noerr_quiet(SecOTRFICreateKeysFromReadPersistentRef(bytes, size, &newID->publicSigningKey, &newID->privateSigningKey), fail); 288 289 return errSecSuccess; 290 291fail: 292 CFReleaseNull(newID->publicSigningKey); 293 CFReleaseNull(newID->privateSigningKey); 294 295 return errSecParam; 296} 297 298static 299OSStatus SecOTRFIInitFromV2Bytes(SecOTRFullIdentityRef newID, CFAllocatorRef allocator, 300 const uint8_t **bytes,size_t *size) { 301 require(**bytes == 2, fail); 302 ++*bytes; 303 --*size; 304 305 require_noerr_quiet(SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(bytes, size, &newID->publicSigningKey, &newID->privateSigningKey, &CreateECPublicKeyFrom), fail); 306 307 return errSecSuccess; 308 309fail: 310 CFReleaseNull(newID->publicSigningKey); 311 CFReleaseNull(newID->privateSigningKey); 312 313 return errSecParam; 314} 315 316SecOTRFullIdentityRef SecOTRFullIdentityCreateFromSecKeyRef(CFAllocatorRef allocator, SecKeyRef privateKey, 317 CFErrorRef *error) { 318 // TODO - make sure this is an appropriate key type 319 SecOTRFullIdentityRef newID = CFTypeAllocate(SecOTRFullIdentity, struct _SecOTRFullIdentity, allocator); 320 newID->privateSigningKey = privateKey; 321 CFRetain(newID->privateSigningKey); 322 newID->publicSigningKey = SecKeyCreatePublicFromPrivate(privateKey); 323 require(SecOTRFICachePublicHash(newID, error), fail); 324 return newID; 325fail: 326 CFRelease(newID->privateSigningKey); 327 CFRelease(newID->publicSigningKey); 328 CFReleaseSafe(newID); 329 return NULL; 330} 331 332SecOTRFullIdentityRef SecOTRFullIdentityCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t *size, CFErrorRef *error) 333{ 334 SecOTRFullIdentityRef newID = CFTypeAllocate(SecOTRFullIdentity, struct _SecOTRFullIdentity, allocator); 335 EnsureOTRAlgIDInited(); 336 337 require(*size > 0, fail); 338 339 switch (**bytes) { 340 case 1: 341 require_noerr_quiet(SecOTRFIInitFromV1Bytes(newID, allocator, bytes, size), fail); 342 break; 343 case 2: 344 require_noerr_quiet(SecOTRFIInitFromV2Bytes(newID, allocator, bytes, size), fail); 345 break; 346 case 0: // Version 0 was used in seeds of 5.0, transition from those seeds unsupported - keys were in exported data. 347 default: 348 require(false, fail); 349 break; 350 } 351 352 require(SecOTRFICachePublicHash(newID, error), fail); 353 354 return newID; 355 356fail: 357 if (NULL != newID) { 358 SecOTRFIPurgeFromKeychain(newID, NULL); 359 } 360 CFReleaseSafe(newID); 361 return NULL; 362} 363 364SecOTRFullIdentityRef SecOTRFullIdentityCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) 365{ 366 if (data == NULL) 367 return NULL; 368 369 size_t size = (size_t)CFDataGetLength(data); 370 const uint8_t* bytes = CFDataGetBytePtr(data); 371 372 return SecOTRFullIdentityCreateFromBytes(allocator, &bytes, &size, error); 373} 374 375bool SecOTRFIPurgeFromKeychain(SecOTRFullIdentityRef thisID, CFErrorRef *error) 376{ 377 OSStatus result = SecOTRFIPurgeFromKeychainByValue(thisID->privateSigningKey, sSigningKeyName); 378 if (errSecSuccess == result) { 379 return true; 380 } else { 381 SecOTRCreateError(secOTRErrorOSError, result, CFSTR("OSStatus returned in error code"), NULL, error); 382 return false; 383 } 384} 385 386 387static OSStatus SecOTRFIPurgeAllFromKeychainByLabel(CFStringRef label) 388{ 389 OSStatus status; 390 const void *keys[] = { kSecClass, 391 kSecAttrKeyClass, 392 kSecAttrLabel, 393 }; 394 const void *values[] = { kSecClassKey, 395 kSecAttrKeyClassPrivate, 396 label, 397 }; 398 CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, array_size(values), NULL, NULL); 399 bool deleteAtLeastOne = false; 400 int loopLimiter = 500; 401 do { 402 status = SecItemDelete(dict); 403 if (status == errSecSuccess) { 404 deleteAtLeastOne = true; 405 } 406 loopLimiter--; 407 } while ((status == errSecSuccess) && (loopLimiter > 0)); 408 if ((status == errSecItemNotFound) && (deleteAtLeastOne)) { 409 // We've looped until we can't delete any more keys. 410 // Since this will produce an expected 'itemNotFound', but we don't want to break the contract above 411 // (and also we want to make sense) 412 // we muffle the non-error to a success case, which it is. 413 status = errSecSuccess; 414 } 415 CFReleaseSafe(dict); 416 417 return status; 418} 419 420bool SecOTRFIPurgeAllFromKeychain(CFErrorRef *error) 421{ 422 OSStatus result = SecOTRFIPurgeAllFromKeychainByLabel(sSigningKeyName); 423 if (errSecSuccess == result) { 424 return true; 425 } else { 426 SecOTRCreateError(secOTRErrorOSError, result, CFSTR("OSStatus returned in error code"), NULL, error); 427 return false; 428 } 429} 430 431static OSStatus appendPersistentRefData(SecKeyRef theKey, CFMutableDataRef serializeInto, CFStringRef name) 432{ 433 OSStatus status; 434 CFDataRef persistent_ref = NULL; 435 require_noerr(status = SecKeyCopyPersistentRef(theKey, &persistent_ref), fail); 436 437 status = appendSizeAndData(persistent_ref, serializeInto); 438 439fail: 440 CFReleaseSafe(persistent_ref); 441 442 return status; 443} 444 445static OSStatus SecOTRFIAppendV2Serialization(SecOTRFullIdentityRef fullID, CFMutableDataRef serializeInto) 446{ 447 const uint8_t version = 2; 448 CFIndex start = CFDataGetLength(serializeInto); 449 450 CFDataAppendBytes(serializeInto, &version, sizeof(version)); 451 452 require(errSecSuccess == appendPersistentRefData(fullID->privateSigningKey, serializeInto, sSigningKeyName), fail); 453 require(errSecSuccess == appendPublicOctetsAndSize(fullID->publicSigningKey, serializeInto), fail); 454 return errSecSuccess; 455 456fail: 457 CFDataSetLength(serializeInto, start); 458 459 return errSecParam; 460} 461 462 463bool SecOTRFIAppendSerialization(SecOTRFullIdentityRef fullID, CFMutableDataRef serializeInto, CFErrorRef *error) 464{ 465 OSStatus status = SecOTRFIAppendV2Serialization(fullID, serializeInto); 466 if (errSecSuccess == status) { 467 return true; 468 } else { 469 SecOTRCreateError(secOTRErrorOSError, status, CFSTR("OSStatus returned in error code"), NULL, error); 470 return false; 471 } 472} 473 474size_t SecOTRFISignatureSize(SecOTRFullIdentityRef fullID) 475{ 476 return SecKeyGetSize(fullID->publicSigningKey, kSecKeySignatureSize); 477} 478 479bool SecOTRFIAppendSignature(SecOTRFullIdentityRef fullID, 480 CFDataRef dataToHash, 481 CFMutableDataRef appendTo, 482 CFErrorRef *error) 483{ 484 const size_t signatureSize = SecOTRFISignatureSize(fullID); 485 const CFIndex sourceLength = CFDataGetLength(dataToHash); 486 const uint8_t* sourceData = CFDataGetBytePtr(dataToHash); 487 488 CFIndex start = CFDataGetLength(appendTo); 489 490 require(((CFIndex)signatureSize) >= 0, fail); 491 492 CFDataIncreaseLength(appendTo, (CFIndex)signatureSize + 1); 493 494 uint8_t *size = CFDataGetMutableBytePtr(appendTo) + start; 495 uint8_t* signatureStart = size + 1; 496 size_t signatureUsed = signatureSize; 497 498 require(SecKeyDigestAndSignWithError(fullID->privateSigningKey, kOTRSignatureAlgIDPtr, 499 sourceData, (size_t)sourceLength, 500 signatureStart, &signatureUsed, error), fail); 501 502 require(signatureUsed < 256, fail); 503 require(((CFIndex)signatureUsed) >= 0, fail); 504 *size = signatureUsed; 505 506 CFDataSetLength(appendTo, start + (CFIndex)signatureUsed + 1); 507 508 return true; 509 510fail: 511 CFDataSetLength(appendTo, start); 512 513 return false; 514} 515 516void SecOTRFIAppendPublicHash(SecOTRFullIdentityRef fullID, CFMutableDataRef appendTo) 517{ 518 CFDataAppendBytes(appendTo, fullID->publicIDHash, sizeof(fullID->publicIDHash)); 519} 520 521bool SecOTRFIComparePublicHash(SecOTRFullIdentityRef fullID, const uint8_t hash[kMPIDHashSize]) 522{ 523 return 0 == memcmp(hash, fullID->publicIDHash, kMPIDHashSize); 524} 525