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