/*
* 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 <AssertMacros.h>
#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
#include <Security/SecureObjectSync/SOSPeerInfoV2.h>
#include <Security/SecureObjectSync/SOSPeerInfoDER.h>
#include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
#include <Security/SecureObjectSync/SOSCircle.h>
#include <Security/SecureObjectSync/SOSInternal.h>
#include <Security/SecureObjectSync/SOSPeerInfoDER.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecItemPriv.h>
#include <Security/SecOTR.h>
#include <CoreFoundation/CFArray.h>
#include <dispatch/dispatch.h>
#include <Security/SecFramework.h>
#include <stdlib.h>
#include <assert.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCFRelease.h>
#include <utilities/der_plist.h>
#include <utilities/der_plist_internal.h>
#include <corecrypto/ccder.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
#include <CoreFoundation/CoreFoundation.h>
#include "utilities/iOSforOSX.h"
#include <AssertMacros.h>
#include <utilities/SecCFError.h>
// 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;
CFDataRef octagon_peer_signing_key_ref;
CFDataRef octagon_peer_encryption_key_ref;
};
CFGiblisWithHashFor(SOSFullPeerInfo);
CFStringRef kSOSFullPeerInfoDescriptionKey = CFSTR("SOSFullPeerInfoDescription");
CFStringRef kSOSFullPeerInfoSignatureKey = CFSTR("SOSFullPeerInfoSignature");
CFStringRef kSOSFullPeerInfoNameKey = CFSTR("SOSFullPeerInfoName");
bool SOSFullPeerInfoUpdate(SOSFullPeerInfoRef fullPeerInfo, CFErrorRef *error, SOSPeerInfoRef (^create_modification)(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error)) {
bool result = false;
SOSPeerInfoRef newPeer = NULL;
SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(fullPeerInfo, error);
require_quiet(device_key, fail);
newPeer = create_modification(fullPeerInfo->peer_info, device_key, error);
require_quiet(newPeer, fail);
CFTransferRetained(fullPeerInfo->peer_info, newPeer);
result = true;
fail:
CFReleaseNull(device_key);
CFReleaseNull(newPeer);
return result;
}
bool SOSFullPeerInfoUpdateToThisPeer(SOSFullPeerInfoRef peer, SOSPeerInfoRef pi, CFErrorRef *error) {
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoSign(key, pi, error) ? CFRetainSafe(pi): NULL;
});
}
SOSFullPeerInfoRef SOSFullPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt,
CFDataRef backupKey,
SecKeyRef signingKey,
SecKeyRef octagonPeerSigningKey,
SecKeyRef octagonPeerEncryptionKey,
CFErrorRef* error) {
return SOSFullPeerInfoCreateWithViews(allocator, gestalt, backupKey, NULL, signingKey,
octagonPeerSigningKey, octagonPeerEncryptionKey, error);
}
SOSFullPeerInfoRef SOSFullPeerInfoCreateWithViews(CFAllocatorRef allocator,
CFDictionaryRef gestalt, CFDataRef backupKey, CFSetRef initialViews,
SecKeyRef signingKey,
SecKeyRef octagonPeerSigningKey,
SecKeyRef octagonPeerEncryptionKey,
CFErrorRef* error) {
SOSFullPeerInfoRef result = NULL;
SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, allocator);
CFStringRef IDSID = CFSTR("");
CFStringRef transportType = SOSTransportMessageTypeKVS;
CFBooleanRef preferIDS = kCFBooleanFalse;
CFBooleanRef preferIDSFragmentation = kCFBooleanTrue;
CFBooleanRef preferACKModel = kCFBooleanTrue;
fpi->peer_info = SOSPeerInfoCreateWithTransportAndViews(allocator, gestalt, backupKey,
IDSID, transportType, preferIDS,
preferIDSFragmentation, preferACKModel, initialViews,
signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error);
require_quiet(fpi->peer_info, exit);
OSStatus status = SecKeyCopyPersistentRef(signingKey, &fpi->key_ref);
require_quiet(SecError(status, error, CFSTR("Inflating persistent ref")), exit);
status = SecKeyCopyPersistentRef(octagonPeerSigningKey, &fpi->octagon_peer_signing_key_ref);
require_quiet(SecError(status, error, CFSTR("Inflating octagon peer signing persistent ref")), exit);
status = SecKeyCopyPersistentRef(octagonPeerSigningKey, &fpi->octagon_peer_encryption_key_ref);
require_quiet(SecError(status, error, CFSTR("Inflating octagon peer encryption persistent ref")), exit);
CFTransferRetained(result, fpi);
exit:
CFReleaseNull(fpi);
return result;
}
SOSFullPeerInfoRef SOSFullPeerInfoCopyFullPeerInfo(SOSFullPeerInfoRef toCopy) {
SOSFullPeerInfoRef retval = NULL;
SOSFullPeerInfoRef fpi = CFTypeAllocate(SOSFullPeerInfo, struct __OpaqueSOSFullPeerInfo, kCFAllocatorDefault);
SOSPeerInfoRef piToCopy = SOSFullPeerInfoGetPeerInfo(toCopy);
require_quiet(piToCopy, errOut);
require_quiet(fpi, errOut);
fpi->peer_info = SOSPeerInfoCreateCopy(kCFAllocatorDefault, piToCopy, NULL);
require_quiet(fpi->peer_info, errOut);
fpi->key_ref = CFRetainSafe(toCopy->key_ref);
CFTransferRetained(retval, fpi);
errOut:
CFReleaseNull(fpi);
return retval;
}
bool SOSFullPeerInfoUpdateOctagonSigningKey(SOSFullPeerInfoRef peer, SecKeyRef octagonSigningKey, CFErrorRef* error){
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoSetOctagonSigningKey(kCFAllocatorDefault, peer, octagonSigningKey, key, error);
});
}
bool SOSFullPeerInfoUpdateOctagonEncryptionKey(SOSFullPeerInfoRef peer, SecKeyRef octagonEncryptionKey, CFErrorRef* error){
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoSetOctagonEncryptionKey(kCFAllocatorDefault, peer, octagonEncryptionKey, key, error);
});
}
CFDataRef SOSPeerInfoCopyData(SOSPeerInfoRef pi, CFErrorRef *error)
{
CFTypeRef vData = NULL;
SecKeyRef pubKey = SOSPeerInfoCopyPubKey(pi, error);
CFDictionaryRef query = NULL;
require_quiet(pubKey, exit);
CFDataRef public_key_hash = SecKeyCopyPublicKeyHash(pubKey);
query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
kSecClass, kSecClassKey,
kSecAttrKeyClass, kSecAttrKeyClassPrivate,
kSecAttrSynchronizable, kSecAttrSynchronizableAny,
kSecAttrApplicationLabel, public_key_hash,
kSecReturnData, kCFBooleanTrue,
NULL);
CFReleaseNull(public_key_hash);
require_quiet(SecError(SecItemCopyMatching(query, &vData),error ,
CFSTR("Error finding persistent ref to key from public:
exit:
CFReleaseNull(query);
CFReleaseNull(pubKey);
secnotice("fpi","no private key found");
return (CFDataRef)vData;
}
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, error);
require_quiet(pubKey, exit);
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);
const uint8_t *sequence_end;
*der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
CFReleaseNull(fpi->peer_info);
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);
require_quiet(*der_p != NULL, fail);
return fpi;
fail:
CFReleaseNull(fpi);
return NULL;
}
SOSFullPeerInfoRef SOSFullPeerInfoCreateFromData(CFAllocatorRef allocator, CFDataRef fullPeerData, CFErrorRef *error)
{
if(!fullPeerData) return NULL;
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);
CFReleaseNull(fpi->octagon_peer_signing_key_ref);
CFReleaseNull(fpi->octagon_peer_encryption_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 SOSFullPeerInfoCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
SOSFullPeerInfoRef fpi = (SOSFullPeerInfoRef) aObj;
return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSFullPeerInfo@}
bool SOSFullPeerInfoUpdateGestalt(SOSFullPeerInfoRef peer, CFDictionaryRef gestalt, CFErrorRef* error)
{
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoCopyWithGestaltUpdate(kCFAllocatorDefault, peer, gestalt, key, error);
});
}
bool SOSFullPeerInfoUpdateV2Dictionary(SOSFullPeerInfoRef peer, CFDictionaryRef newv2dict, CFErrorRef* error)
{
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoCopyWithV2DictionaryUpdate(kCFAllocatorDefault, peer,
newv2dict, key, error);
});
}
bool SOSFullPeerInfoUpdateBackupKey(SOSFullPeerInfoRef peer, CFDataRef backupKey, CFErrorRef* error)
{
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoCopyWithBackupKeyUpdate(kCFAllocatorDefault, peer, backupKey, key, error);
});
}
bool SOSFullPeerInfoAddEscrowRecord(SOSFullPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord, CFErrorRef* error)
{
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoCopyWithEscrowRecordUpdate(kCFAllocatorDefault, peer, dsid, escrowRecord, key, error);
});
}
bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error)
{
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoCopyWithReplacedEscrowRecords(kCFAllocatorDefault, peer, escrowRecords, key, error);
});
}
SOSViewResultCode SOSFullPeerInfoUpdateViews(SOSFullPeerInfoRef peer, SOSViewActionCode action, CFStringRef viewname, CFErrorRef* error)
{
__block SOSViewResultCode retval = kSOSCCGeneralViewError;
secnotice("viewChange", "
return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
return SOSPeerInfoCopyWithViewsChange(kCFAllocatorDefault, peer, action, viewname, &retval, key, error);
}) ? retval : kSOSCCGeneralViewError;
}
static CFMutableSetRef SOSFullPeerInfoCopyViewUpdate(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews) {
CFSetRef enabledViews = SOSPeerInfoCopyEnabledViews(peer->peer_info);
CFMutableSetRef newViews = SOSPeerInfoCopyEnabledViews(peer->peer_info);
if (isSet(minimumViews)) {
CFSetUnion(newViews, minimumViews);
}
if (isSet(excludedViews)) {
CFSetSubtract(newViews, excludedViews);
}
if (CFEqualSafe(newViews, enabledViews)) {
CFReleaseNull(newViews);
}
CFReleaseNull(enabledViews);
return newViews;
}
static bool SOSFullPeerInfoNeedsViewUpdate(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews) {
CFSetRef updatedViews = SOSFullPeerInfoCopyViewUpdate(peer, minimumViews, excludedViews);
bool needsUpdate = (updatedViews != NULL);
CFReleaseNull(updatedViews);
return needsUpdate;
}
static bool sosFullPeerInfoRequiresUpdate(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews) {
if(!SOSPeerInfoVersionIsCurrent(peer->peer_info)) return true;
if(!SOSPeerInfoSerialNumberIsSet(peer->peer_info)) return true;
if(SOSFullPeerInfoNeedsViewUpdate(peer, minimumViews, excludedViews)) return true;
return false;
}
// Returning false indicates we don't need to upgrade.
bool SOSFullPeerInfoUpdateToCurrent(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews) {
bool success = false;
CFMutableSetRef newViews = NULL;
CFErrorRef copyError = NULL;
CFErrorRef createError = NULL;
SecKeyRef device_key = NULL;
require_quiet(sosFullPeerInfoRequiresUpdate(peer, minimumViews, excludedViews), errOut);
newViews = SOSFullPeerInfoCopyViewUpdate(peer, minimumViews, excludedViews);
device_key = SOSFullPeerInfoCopyDeviceKey(peer, ©Error);
require_action_quiet(device_key, errOut,
secnotice("upgrade", "SOSFullPeerInfoCopyDeviceKey failed:
SOSPeerInfoRef newPeer = SOSPeerInfoCreateCurrentCopy(kCFAllocatorDefault, peer->peer_info,
NULL, NULL, kCFBooleanFalse, kCFBooleanTrue, kCFBooleanTrue, newViews,
device_key, &createError);
require_action_quiet(newPeer, errOut,
secnotice("upgrade", "Peer info v2 create copy failed:
CFTransferRetained(peer->peer_info, newPeer);
success = true;
errOut:
CFReleaseNull(newViews);
CFReleaseNull(copyError);
CFReleaseNull(createError);
CFReleaseNull(device_key);
return success;
}
SOSViewResultCode SOSFullPeerInfoViewStatus(SOSFullPeerInfoRef peer, CFStringRef viewname, CFErrorRef *error)
{
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(peer);
if(!pi) return kSOSCCGeneralViewError;
return SOSPeerInfoViewStatus(pi, viewname, error);
}
SOSPeerInfoRef SOSFullPeerInfoGetPeerInfo(SOSFullPeerInfoRef fullPeer) {
return fullPeer?fullPeer->peer_info:NULL;
}
// MARK: Private Key Retrieval and Existence
SecKeyRef SOSFullPeerInfoCopyPubKey(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
SecKeyRef retval = NULL;
require_quiet(fpi, errOut);
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
require_quiet(pi, errOut);
retval = SOSPeerInfoCopyPubKey(pi, error);
errOut:
return retval;
}
SecKeyRef SOSFullPeerInfoCopyOctagonPublicSigningKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
{
SecKeyRef retval = NULL;
require_quiet(fpi, errOut);
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
require_quiet(pi, errOut);
retval = SOSPeerInfoCopyOctagonSigningPublicKey(pi, error);
errOut:
return retval;
}
SecKeyRef SOSFullPeerInfoCopyOctagonPublicEncryptionKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
{
SecKeyRef retval = NULL;
require_quiet(fpi, errOut);
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fpi);
require_quiet(pi, errOut);
retval = SOSPeerInfoCopyOctagonEncryptionPublicKey(pi, error);
errOut:
return retval;
}
static SecKeyRef SOSFullPeerInfoCopyMatchingPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
SecKeyRef retval = NULL;
SecKeyRef pub = SOSFullPeerInfoCopyPubKey(fpi, error);
require_quiet(pub, exit);
retval = SecKeyCopyMatchingPrivateKey(pub, error);
exit:
CFReleaseNull(pub);
return retval;
}
static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonSigningPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
{
SecKeyRef retval = NULL;
SecKeyRef pub = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, error);
require_quiet(pub, exit);
retval = SecKeyCopyMatchingPrivateKey(pub, error);
exit:
CFReleaseNull(pub);
return retval;
}
static SecKeyRef SOSFullPeerInfoCopyMatchingOctagonEncryptionPrivateKey(SOSFullPeerInfoRef fpi, CFErrorRef* error)
{
SecKeyRef retval = NULL;
SecKeyRef pub = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(fpi, error);
require_quiet(pub, exit);
retval = SecKeyCopyMatchingPrivateKey(pub, error);
exit:
CFReleaseNull(pub);
return retval;
}
static OSStatus SOSFullPeerInfoGetMatchingPrivateKeyStatus(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
OSStatus retval = errSecParam;
SecKeyRef pub = SOSFullPeerInfoCopyPubKey(fpi, error);
require_quiet(pub, exit);
retval = SecKeyGetMatchingPrivateKeyStatus(pub, error);
exit:
CFReleaseNull(pub);
return retval;
}
bool SOSFullPeerInfoValidate(SOSFullPeerInfoRef peer, CFErrorRef* error) {
OSStatus result = SOSFullPeerInfoGetMatchingPrivateKeyStatus(peer, error);
if(result == errSecSuccess) return true;
return false;
}
bool SOSFullPeerInfoPrivKeyExists(SOSFullPeerInfoRef peer) {
OSStatus result = SOSFullPeerInfoGetMatchingPrivateKeyStatus(peer, NULL);
if(result == errSecItemNotFound || result == errSecParam) return false;
return true;
}
bool SOSFullPeerInfoPurgePersistentKey(SOSFullPeerInfoRef fpi, CFErrorRef* error) {
bool result = false;
CFDictionaryRef privQuery = NULL;
CFMutableDictionaryRef query = NULL;
CFDictionaryRef octagonPrivQuery = NULL;
CFMutableDictionaryRef octagonQuery = NULL;
SecKeyRef pub = SOSFullPeerInfoCopyPubKey(fpi, error);
SecKeyRef octagonSigningPub = SOSFullPeerInfoCopyOctagonPublicSigningKey(fpi, error);
SecKeyRef octagonEncryptionPub = SOSFullPeerInfoCopyOctagonPublicEncryptionKey(fpi, error);
require_quiet(pub, fail);
// iCloud Identities doesn't have either signing or encryption key here. Don't fail if they're not present.
privQuery = CreatePrivateKeyMatchingQuery(pub, false);
query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, privQuery);
CFDictionaryAddValue(query, kSecUseTombstones, kCFBooleanFalse);
result = SecError(SecItemDelete(query), error, CFSTR("Deleting while purging"));
// do the same thing to also purge the octagon sync signing key
if(octagonSigningPub) {
octagonPrivQuery = CreatePrivateKeyMatchingQuery(octagonSigningPub, false);
octagonQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, octagonPrivQuery);
CFDictionaryAddValue(octagonQuery, kSecUseTombstones, kCFBooleanFalse);
result &= SecError(SecItemDelete(octagonQuery), error, CFSTR("Deleting signing key while purging"));
}
CFReleaseNull(octagonPrivQuery);
CFReleaseNull(octagonQuery);
// do the same thing to also purge the octagon encryption key
if(octagonEncryptionPub) {
octagonPrivQuery = CreatePrivateKeyMatchingQuery(octagonEncryptionPub, false);
octagonQuery = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, octagonPrivQuery);
CFDictionaryAddValue(octagonQuery, kSecUseTombstones, kCFBooleanFalse);
result &= SecError(SecItemDelete(octagonQuery), error, CFSTR("Deleting encryption key while purging"));
}
fail:
CFReleaseNull(privQuery);
CFReleaseNull(query);
CFReleaseNull(pub);
CFReleaseNull(octagonPrivQuery);
CFReleaseNull(octagonQuery);
CFReleaseNull(octagonSigningPub);
CFReleaseNull(octagonEncryptionPub);
return result;
}
SecKeyRef SOSFullPeerInfoCopyDeviceKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
{
return SOSFullPeerInfoCopyMatchingPrivateKey(fullPeer, error);
}
SecKeyRef SOSFullPeerInfoCopyOctagonSigningKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
{
return SOSFullPeerInfoCopyMatchingOctagonSigningPrivateKey(fullPeer, error);
}
SecKeyRef SOSFullPeerInfoCopyOctagonEncryptionKey(SOSFullPeerInfoRef fullPeer, CFErrorRef* error)
{
return SOSFullPeerInfoCopyMatchingOctagonEncryptionPrivateKey(fullPeer, error);
}
bool SOSFullPeerInfoHaveOctagonKeys(SOSFullPeerInfoRef fullPeer)
{
SOSPeerInfoRef pi = SOSFullPeerInfoGetPeerInfo(fullPeer);
if (pi == NULL) {
return false;
}
return
SOSPeerInfoHasOctagonSigningPubKey(pi) &&
SOSPeerInfoHasOctagonEncryptionPubKey(pi);
}
//
// 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)
{
return CFDataCreateWithDER(kCFAllocatorDefault, SOSFullPeerInfoGetDEREncodedSize(peer, error), ^uint8_t*(size_t size, uint8_t *buffer) {
return SOSFullPeerInfoEncodeToDER(peer, error, buffer, (uint8_t *) buffer + size);
});
}
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;
}
bool SOSFullPeerInfoPing(SOSFullPeerInfoRef peer, CFErrorRef* error) {
bool retval = false;
SecKeyRef device_key = SOSFullPeerInfoCopyDeviceKey(peer, error);
require_quiet(device_key, fail);
SOSPeerInfoRef newPeer = SOSPeerInfoCopyWithPing(kCFAllocatorDefault, peer->peer_info, device_key, error);
require_quiet(newPeer, fail);
CFReleaseNull(peer->peer_info);
peer->peer_info = newPeer;
newPeer = NULL;
retval = true;
fail:
CFReleaseNull(device_key);
return retval;
}