1// 2// SOSFullPeerInfo.c 3// sec 4// 5// Created by Mitch Adler on 10/26/12. 6// 7// 8 9#include <AssertMacros.h> 10 11#include <SecureObjectSync/SOSFullPeerInfo.h> 12#include <SecureObjectSync/SOSCircle.h> 13 14#include <SecureObjectSync/SOSInternal.h> 15 16#include <Security/SecKeyPriv.h> 17#include <Security/SecItemPriv.h> 18#include <Security/SecOTR.h> 19#include <CoreFoundation/CFArray.h> 20#include <dispatch/dispatch.h> 21 22#include <stdlib.h> 23#include <assert.h> 24 25#include <utilities/SecCFWrappers.h> 26#include <utilities/SecCFRelease.h> 27 28#include <utilities/der_plist.h> 29#include <utilities/der_plist_internal.h> 30#include <corecrypto/ccder.h> 31 32#include <CommonCrypto/CommonDigest.h> 33#include <CommonCrypto/CommonDigestSPI.h> 34 35#include <CoreFoundation/CoreFoundation.h> 36 37#include "utilities/iOSforOSX.h" 38 39#include <AssertMacros.h> 40 41#include <utilities/SecCFError.h> 42 43// for OS X 44#ifdef __cplusplus 45extern "C" { 46#endif 47 48//---- missing 49 50extern OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* publicBytes); 51extern SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey); 52#ifdef __cplusplus 53} 54#endif 55 56struct __OpaqueSOSFullPeerInfo { 57 CFRuntimeBase _base; 58 59 SOSPeerInfoRef peer_info; 60 CFDataRef key_ref; 61}; 62 63CFGiblisWithHashFor(SOSFullPeerInfo); 64 65 66static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey"); 67static CFStringRef sNameKey = CFSTR("DeviceName"); 68static CFStringRef sVersionKey = CFSTR("ConflictVersion"); 69 70CFStringRef kSOSFullPeerInfoDescriptionKey = CFSTR("SOSFullPeerInfoDescription"); 71CFStringRef kSOSFullPeerInfoSignatureKey = CFSTR("SOSFullPeerInfoSignature"); 72CFStringRef kSOSFullPeerInfoNameKey = CFSTR("SOSFullPeerInfoName"); 73 74SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { 75 SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator); 76 77 fpi->peer_info = SOSPeerInfoCreate(allocator, gestalt, signingKey, error); 78 if (fpi->peer_info == NULL) { 79 CFReleaseNull(fpi); 80 goto exit; 81 } 82 83 OSStatus result = SecKeyCopyPersistentRef(signingKey, &fpi->key_ref); 84 85 if (result != errSecSuccess) { 86 CFReleaseNull(fpi); 87 goto exit; 88 } 89 90exit: 91 return fpi; 92} 93 94 95 96SOSFullPeerInfoRef SOSFullPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, SOSPeerInfoRef peer, CFErrorRef* error) { 97 SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator); 98 99 SecKeyRef pubKey = NULL; 100 101 fpi->peer_info = peer; 102 CFRetainSafe(fpi->peer_info); 103 if (fpi->peer_info == NULL) { 104 CFReleaseNull(fpi); 105 goto exit; 106 } 107 108 pubKey = SOSPeerInfoCopyPubKey(peer); 109 110 fpi->key_ref = SecKeyCreatePersistentRefToMatchingPrivateKey(pubKey, error); 111 112 if (fpi->key_ref == NULL) { 113 CFReleaseNull(fpi); 114 goto exit; 115 } 116 117exit: 118 CFReleaseNull(pubKey); 119 return fpi; 120} 121 122 123SOSFullPeerInfoRef SOSFullPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error, 124 const uint8_t** der_p, const uint8_t *der_end) { 125 SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator); 126 127 const uint8_t *sequence_end; 128 129 *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); 130 131 fpi->peer_info = SOSPeerInfoCreateFromDER(allocator, error, der_p, der_end); 132 require_quiet(fpi->peer_info != NULL, fail); 133 134 *der_p = der_decode_data(allocator, kCFPropertyListImmutable, &fpi->key_ref, error, *der_p, sequence_end); 135 require_quiet(*der_p != NULL, fail); 136 137 return fpi; 138 139fail: 140 CFReleaseNull(fpi); 141 return NULL; 142} 143 144SOSFullPeerInfoRef SOSFullPeerInfoCreateFromData(CFAllocatorRef allocator, CFDataRef fullPeerData, CFErrorRef *error) 145{ 146 size_t size = CFDataGetLength(fullPeerData); 147 const uint8_t *der = CFDataGetBytePtr(fullPeerData); 148 SOSFullPeerInfoRef inflated = SOSFullPeerInfoCreateFromDER(allocator, error, &der, der + size); 149 return inflated; 150} 151 152static void SOSFullPeerInfoDestroy(CFTypeRef aObj) { 153 SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj; 154 155 CFReleaseNull(fpi->peer_info); 156 CFReleaseNull(fpi->key_ref); 157} 158 159static Boolean SOSFullPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) { 160 SOSFullPeerInfoRef lpeer = (SOSFullPeerInfoRef) lhs; 161 SOSFullPeerInfoRef rpeer = (SOSFullPeerInfoRef) rhs; 162 163 if (!CFEqual(lpeer->peer_info, rpeer->peer_info)) 164 return false; 165 166 if (CFEqual(lpeer->key_ref, rpeer->key_ref)) 167 return true; 168 169 SecKeyRef lpk = SOSFullPeerInfoCopyDeviceKey(lpeer, NULL); 170 SecKeyRef rpk = SOSFullPeerInfoCopyDeviceKey(rpeer, NULL); 171 172 bool match = lpk && rpk && CFEqual(lpk, rpk); 173 174 CFReleaseNull(lpk); 175 CFReleaseNull(rpk); 176 177 return match; 178} 179 180static CFHashCode SOSFullPeerInfoHash(CFTypeRef cf) { 181 SOSFullPeerInfoRef peer = (SOSFullPeerInfoRef) cf; 182 183 return CFHash(peer->peer_info); 184} 185 186static CFStringRef SOSFullPeerInfoCopyDescription(CFTypeRef aObj) { 187 SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj; 188 189 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSFullPeerInfo@%p: \"%@\">"), fpi, fpi->peer_info); 190} 191 192bool SOSFullPeerInfoUpdateGestalt(SOSFullPeerInfoRef peer, CFDictionaryRef gestalt, CFErrorRef* error) 193{ 194 SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peer, error); 195 require_quiet(device_key, fail); 196 197 SOSPeerInfoRef newPeer = SOSPeerInfoCopyWithGestaltUpdate(kCFAllocatorDefault, peer->peer_info, 198 gestalt, device_key, error); 199 200 require_quiet(newPeer, fail); 201 202 CFReleaseNull(peer->peer_info); 203 peer->peer_info = newPeer; 204 newPeer = NULL; 205 206 CFReleaseNull(device_key); 207 return true; 208 209fail: 210 CFReleaseNull(device_key); 211 return false; 212} 213 214 215bool SOSFullPeerInfoValidate(SOSFullPeerInfoRef peer, CFErrorRef* error) { 216 return true; 217} 218 219bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) { 220 CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, 221 kSecValuePersistentRef, fullPeer->key_ref, 222 kSecUseTombstones, kCFBooleanFalse, 223 NULL); 224 SecItemDelete(query); 225 CFReleaseNull(query); 226 227 return true; 228} 229 230SOSPeerInfoRef SOSFullPeerInfoGetPeerInfo(SOSFullPeerInfoRef fullPeer) 231{ 232 return fullPeer?fullPeer->peer_info:NULL; 233} 234 235SecKeyRef SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) 236{ 237 SecKeyRef device_key = NULL; 238 239 require(fullPeer->key_ref, fail); 240 241 OSStatus result = SecKeyFindWithPersistentRef(fullPeer->key_ref, &device_key); 242 243 require_action_quiet(result == errSecSuccess, fail, SecError(result, error, CFSTR("Finding Persistent Ref"))); 244 245 return device_key; 246 247fail: 248 CFReleaseNull(device_key); 249 return NULL; 250} 251 252// 253// MARK: Encode and decode 254// 255size_t SOSFullPeerInfoGetDEREncodedSize(SOSFullPeerInfoRef peer, CFErrorRef *error) 256{ 257 size_t peer_size = SOSPeerInfoGetDEREncodedSize(peer->peer_info, error); 258 if (peer_size == 0) 259 return 0; 260 261 size_t ref_size = der_sizeof_data(peer->key_ref, error); 262 if (ref_size == 0) 263 return 0; 264 265 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, 266 peer_size + ref_size); 267} 268 269uint8_t* SOSFullPeerInfoEncodeToDER(SOSFullPeerInfoRef peer, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) 270{ 271 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, 272 SOSPeerInfoEncodeToDER(peer->peer_info, error, der, 273 der_encode_data(peer->key_ref, error, der, der_end))); 274} 275 276CFDataRef SOSFullPeerInfoCopyEncodedData(SOSFullPeerInfoRef peer, CFAllocatorRef allocator, CFErrorRef *error) 277{ 278 size_t size = SOSFullPeerInfoGetDEREncodedSize(peer, error); 279 if (size == 0) 280 return NULL; 281 uint8_t buffer[size]; 282 uint8_t* start = SOSFullPeerInfoEncodeToDER(peer, error, buffer, buffer + sizeof(buffer)); 283 CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size); 284 return result; 285} 286 287bool SOSFullPeerInfoPromoteToApplication(SOSFullPeerInfoRef fpi, SecKeyRef user_key, CFErrorRef *error) 288{ 289 bool success = false; 290 SOSPeerInfoRef old_pi = NULL; 291 292 SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fpi, error); 293 require_quiet(device_key, exit); 294 295 old_pi = fpi->peer_info; 296 fpi->peer_info = SOSPeerInfoCopyAsApplication(old_pi, user_key, device_key, error); 297 298 require_action_quiet(fpi->peer_info, exit, fpi->peer_info = old_pi; old_pi = NULL); 299 300 success = true; 301 302exit: 303 CFReleaseSafe(old_pi); 304 CFReleaseSafe(device_key); 305 return success; 306} 307 308bool SOSFullPeerInfoUpgradeSignatures(SOSFullPeerInfoRef fpi, SecKeyRef user_key, CFErrorRef *error) 309{ 310 bool success = false; 311 SOSPeerInfoRef old_pi = NULL; 312 313 SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fpi, error); 314 require_quiet(device_key, exit); 315 316 old_pi = fpi->peer_info; 317 fpi->peer_info = SOSPeerInfoUpgradeSignatures(NULL, user_key, device_key, old_pi, error); 318 319 require_action_quiet(fpi->peer_info, exit, fpi->peer_info = old_pi; old_pi = NULL); 320 321 success = true; 322 323exit: 324 CFReleaseSafe(old_pi); 325 CFReleaseSafe(device_key); 326 return success; 327} 328 329// 330// 331// 332 333SOSPeerInfoRef SOSFullPeerInfoPromoteToRetiredAndCopy(SOSFullPeerInfoRef fpi, CFErrorRef *error) 334{ 335 SOSPeerInfoRef peer_to_free = NULL; 336 SOSPeerInfoRef retired_peer = NULL; 337 SecKeyRef key = SOSFullPeerInfoCopyDeviceKey(fpi, error); 338 require_quiet(key, error_out); 339 340 retired_peer = SOSPeerInfoCreateRetirementTicket(NULL, key, fpi->peer_info, error); 341 342 require_quiet(retired_peer, error_out); 343 344 peer_to_free = fpi->peer_info; 345 fpi->peer_info = retired_peer; 346 CFRetainSafe(fpi->peer_info); 347 348error_out: 349 CFReleaseNull(key); 350 CFReleaseNull(peer_to_free); 351 return retired_peer; 352} 353 354 355 356