/* * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utilities/iOSforOSX.h" #include #include // for OS X #ifdef __cplusplus extern "C" { #endif //---- missing extern OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* publicBytes); extern SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey); #ifdef __cplusplus } #endif struct __OpaqueSOSFullPeerInfo { CFRuntimeBase _base; SOSPeerInfoRef peer_info; CFDataRef key_ref; }; CFGiblisWithHashFor(SOSFullPeerInfo); static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey"); static CFStringRef sNameKey = CFSTR("DeviceName"); static CFStringRef sVersionKey = CFSTR("ConflictVersion"); CFStringRef kSOSFullPeerInfoDescriptionKey = CFSTR("SOSFullPeerInfoDescription"); CFStringRef kSOSFullPeerInfoSignatureKey = CFSTR("SOSFullPeerInfoSignature"); CFStringRef kSOSFullPeerInfoNameKey = CFSTR("SOSFullPeerInfoName"); SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) { SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator); fpi->peer_info = SOSPeerInfoCreate(allocator, gestalt, signingKey, error); if (fpi->peer_info == NULL) { CFReleaseNull(fpi); goto exit; } OSStatus result = SecKeyCopyPersistentRef(signingKey, &fpi->key_ref); if (result != errSecSuccess) { CFReleaseNull(fpi); goto exit; } exit: return fpi; } SOSFullPeerInfoRef SOSFullPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, SOSPeerInfoRef peer, CFErrorRef* error) { SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator); SecKeyRef pubKey = NULL; fpi->peer_info = peer; CFRetainSafe(fpi->peer_info); if (fpi->peer_info == NULL) { CFReleaseNull(fpi); goto exit; } pubKey = SOSPeerInfoCopyPubKey(peer); fpi->key_ref = SecKeyCreatePersistentRefToMatchingPrivateKey(pubKey, error); if (fpi->key_ref == NULL) { CFReleaseNull(fpi); goto exit; } exit: CFReleaseNull(pubKey); return fpi; } SOSFullPeerInfoRef SOSFullPeerInfoCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error, const uint8_t** der_p, const uint8_t *der_end) { SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator); SecKeyRef device_key = NULL; const uint8_t *sequence_end; *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end); fpi->peer_info = SOSPeerInfoCreateFromDER(allocator, error, der_p, der_end); require_quiet(fpi->peer_info != NULL, fail); *der_p = der_decode_data(allocator, kCFPropertyListImmutable, &fpi->key_ref, error, *der_p, sequence_end); OSStatus result = SecKeyFindWithPersistentRef(fpi->key_ref, &device_key); require_quiet(result == errSecSuccess, fail); require_quiet(*der_p != NULL, fail); CFReleaseNull(device_key); return fpi; fail: CFReleaseNull(fpi); CFReleaseNull(device_key); return NULL; } SOSFullPeerInfoRef SOSFullPeerInfoCreateFromData(CFAllocatorRef allocator, CFDataRef fullPeerData, CFErrorRef *error) { size_t size = CFDataGetLength(fullPeerData); const uint8_t *der = CFDataGetBytePtr(fullPeerData); SOSFullPeerInfoRef inflated = SOSFullPeerInfoCreateFromDER(allocator, error, &der, der + size); return inflated; } static void SOSFullPeerInfoDestroy(CFTypeRef aObj) { SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj; CFReleaseNull(fpi->peer_info); CFReleaseNull(fpi->key_ref); } static Boolean SOSFullPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) { SOSFullPeerInfoRef lpeer = (SOSFullPeerInfoRef) lhs; SOSFullPeerInfoRef rpeer = (SOSFullPeerInfoRef) rhs; if (!CFEqual(lpeer->peer_info, rpeer->peer_info)) return false; if (CFEqual(lpeer->key_ref, rpeer->key_ref)) return true; SecKeyRef lpk = SOSFullPeerInfoCopyDeviceKey(lpeer, NULL); SecKeyRef rpk = SOSFullPeerInfoCopyDeviceKey(rpeer, NULL); bool match = lpk && rpk && CFEqual(lpk, rpk); CFReleaseNull(lpk); CFReleaseNull(rpk); return match; } static CFHashCode SOSFullPeerInfoHash(CFTypeRef cf) { SOSFullPeerInfoRef peer = (SOSFullPeerInfoRef) cf; return CFHash(peer->peer_info); } static CFStringRef SOSFullPeerInfoCopyDescription(CFTypeRef aObj) { SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj; return CFStringCreateWithFormat(NULL, NULL, CFSTR(""), fpi, fpi->peer_info); } bool SOSFullPeerInfoUpdateGestalt(SOSFullPeerInfoRef peer, CFDictionaryRef gestalt, CFErrorRef* error) { SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peer, error); require_quiet(device_key, fail); SOSPeerInfoRef newPeer = SOSPeerInfoCopyWithGestaltUpdate(kCFAllocatorDefault, peer->peer_info, gestalt, device_key, error); require_quiet(newPeer, fail); CFReleaseNull(peer->peer_info); peer->peer_info = newPeer; newPeer = NULL; CFReleaseNull(device_key); return true; fail: CFReleaseNull(device_key); return false; } bool SOSFullPeerInfoValidate(SOSFullPeerInfoRef peer, CFErrorRef* error) { return true; } bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) { CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSecValuePersistentRef, fullPeer->key_ref, kSecUseTombstones, kCFBooleanFalse, NULL); SecItemDelete(query); CFReleaseNull(query); CFReleaseNull(fullPeer->key_ref); return true; } SOSPeerInfoRef SOSFullPeerInfoGetPeerInfo(SOSFullPeerInfoRef fullPeer) { return fullPeer?fullPeer->peer_info:NULL; } SecKeyRef SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error) { SecKeyRef device_key = NULL; require(fullPeer && fullPeer->key_ref, fail); OSStatus result = SecKeyFindWithPersistentRef(fullPeer->key_ref, &device_key); require_action_quiet(result == errSecSuccess, fail, SecError(result, error, CFSTR("Finding Persistent Ref"))); return device_key; fail: CFReleaseNull(device_key); return NULL; } // // MARK: Encode and decode // size_t SOSFullPeerInfoGetDEREncodedSize(SOSFullPeerInfoRef peer, CFErrorRef *error) { size_t peer_size = SOSPeerInfoGetDEREncodedSize(peer->peer_info, error); if (peer_size == 0) return 0; size_t ref_size = der_sizeof_data(peer->key_ref, error); if (ref_size == 0) return 0; return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, peer_size + ref_size); } uint8_t* SOSFullPeerInfoEncodeToDER(SOSFullPeerInfoRef peer, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) { return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, SOSPeerInfoEncodeToDER(peer->peer_info, error, der, der_encode_data(peer->key_ref, error, der, der_end))); } CFDataRef SOSFullPeerInfoCopyEncodedData(SOSFullPeerInfoRef peer, CFAllocatorRef allocator, CFErrorRef *error) { size_t size = SOSFullPeerInfoGetDEREncodedSize(peer, error); if (size == 0) return NULL; uint8_t buffer[size]; uint8_t* start = SOSFullPeerInfoEncodeToDER(peer, error, buffer, buffer + sizeof(buffer)); CFDataRef result = CFDataCreate(kCFAllocatorDefault, start, size); return result; } bool SOSFullPeerInfoPromoteToApplication(SOSFullPeerInfoRef fpi, SecKeyRef user_key, CFErrorRef *error) { bool success = false; SOSPeerInfoRef old_pi = NULL; SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fpi, error); require_quiet(device_key, exit); old_pi = fpi->peer_info; fpi->peer_info = SOSPeerInfoCopyAsApplication(old_pi, user_key, device_key, error); require_action_quiet(fpi->peer_info, exit, fpi->peer_info = old_pi; old_pi = NULL); success = true; exit: CFReleaseSafe(old_pi); CFReleaseSafe(device_key); return success; } bool SOSFullPeerInfoUpgradeSignatures(SOSFullPeerInfoRef fpi, SecKeyRef user_key, CFErrorRef *error) { bool success = false; SOSPeerInfoRef old_pi = NULL; SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fpi, error); require_quiet(device_key, exit); old_pi = fpi->peer_info; fpi->peer_info = SOSPeerInfoUpgradeSignatures(NULL, user_key, device_key, old_pi, error); require_action_quiet(fpi->peer_info, exit, fpi->peer_info = old_pi; old_pi = NULL); success = true; exit: CFReleaseSafe(old_pi); CFReleaseSafe(device_key); return success; } // // // SOSPeerInfoRef SOSFullPeerInfoPromoteToRetiredAndCopy(SOSFullPeerInfoRef fpi, CFErrorRef *error) { SOSPeerInfoRef peer_to_free = NULL; SOSPeerInfoRef retired_peer = NULL; SecKeyRef key = SOSFullPeerInfoCopyDeviceKey(fpi, error); require_quiet(key, error_out); retired_peer = SOSPeerInfoCreateRetirementTicket(NULL, key, fpi->peer_info, error); require_quiet(retired_peer, error_out); peer_to_free = fpi->peer_info; fpi->peer_info = retired_peer; CFRetainSafe(fpi->peer_info); error_out: CFReleaseNull(key); CFReleaseNull(peer_to_free); return retired_peer; }