/*
* 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 <TargetConditionals.h>
#include <Security/SecureObjectSync/SOSPeerInfo.h>
#include <Security/SecureObjectSync/SOSPeerInfoInternal.h>
#include <Security/SecureObjectSync/SOSPeerInfoPriv.h>
#include <Security/SecureObjectSync/SOSPeerInfoV2.h>
#include <Security/SecureObjectSync/SOSCircle.h>
#include <Security/SecureObjectSync/SOSInternal.h>
#include <ipc/securityd_client.h>
#include <CoreFoundation/CFArray.h>
#include <dispatch/dispatch.h>
#include <stdlib.h>
#include <assert.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCFRelease.h>
#include <utilities/SecCFError.h>
#include <utilities/SecXPCError.h>
#include <utilities/der_plist.h>
#include <utilities/der_plist_internal.h>
#include <corecrypto/ccder.h>
#include <utilities/der_date.h>
#include <corecrypto/ccdigest.h>
#include <corecrypto/ccsha2.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFDate.h>
#include <xpc/xpc.h>
#if TARGET_OS_IPHONE || TARGET_OS_EMBEDDED
#include <MobileGestalt.h>
#endif
#include <Security/SecBase64.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecOTR.h>
#include <Security/SecuritydXPC.h>
CFGiblisWithHashFor(SOSPeerInfo);
const CFStringRef kPIUserDefinedDeviceNameKey = CFSTR("ComputerName");
const CFStringRef kPIDeviceModelNameKey = CFSTR("ModelName");
const CFStringRef kPIMessageProtocolVersionKey = CFSTR("MessageProtocolVersion");
const CFStringRef kPIOSVersionKey = CFSTR("OSVersion");
// Description Dictionary Entries
static CFStringRef sPublicKeyKey = CFSTR("PublicSigningKey");
static CFStringRef sOctagonPeerSigningPublicKeyKey = CFSTR("OctagonPublicSigningKey");
static CFStringRef sOctagonPeerEncryptionPublicKeyKey = CFSTR("OctagonPublicEncryptionKey");
const CFStringRef sGestaltKey = CFSTR("DeviceGestalt");
const CFStringRef sVersionKey = CFSTR("ConflictVersion");
static CFStringRef sCloudIdentityKey = CFSTR("CloudIdentity");
static CFStringRef sApplicationDate = CFSTR("ApplicationDate");
static CFStringRef sApplicationUsig = CFSTR("ApplicationUsig");
static CFStringRef sRetirementDate = CFSTR("RetirementDate");
// Peerinfo Entries
CFStringRef kSOSPeerInfoDescriptionKey = CFSTR("SOSPeerInfoDescription");
CFStringRef kSOSPeerInfoSignatureKey = CFSTR("SOSPeerInfoSignature");
CFStringRef kSOSPeerInfoNameKey = CFSTR("SOSPeerInfoName");
//Peer Info V2 Dictionary IDS keys
CFStringRef sPreferIDS = CFSTR("PreferIDS");
CFStringRef sPreferIDSFragmentation = CFSTR("PreferIDFragmentation");
CFStringRef sPreferIDSACKModel = CFSTR("PreferIDSAckModel");
CFStringRef sTransportType = CFSTR("TransportType");
CFStringRef sDeviceID = CFSTR("DeviceID");
const CFStringRef peerIDLengthKey = CFSTR("idLength");
SOSPeerInfoRef SOSPeerInfoAllocate(CFAllocatorRef allocator) {
return CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
}
static SecKeyRef _SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFStringRef keyDictionaryKey, CFErrorRef* error)
{
SecKeyRef result = NULL;
CFDataRef pubKeyBytes = asData(CFDictionaryGetValue(peer->description, keyDictionaryKey), error);
require_quiet(pubKeyBytes, fail);
CFAllocatorRef allocator = CFGetAllocator(peer);
result = SecKeyCreateFromPublicData(allocator, kSecECDSAAlgorithmID, pubKeyBytes);
require_quiet(SecAllocationError(result, error, CFSTR("Failed to create public key from data
fail:
return result;
}
SecKeyRef SOSPeerInfoCopyPubKey(SOSPeerInfoRef peer, CFErrorRef* error) {
return _SOSPeerInfoCopyPubKey(peer, sPublicKeyKey, error);
}
SecKeyRef SOSPeerInfoCopyOctagonSigningPublicKey(SOSPeerInfoRef peer, CFErrorRef* error)
{
return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerSigningPublicKeyKey, error);
}
SecKeyRef SOSPeerInfoCopyOctagonEncryptionPublicKey(SOSPeerInfoRef peer, CFErrorRef* error)
{
return _SOSPeerInfoCopyPubKey(peer, sOctagonPeerEncryptionPublicKeyKey, error);
}
CFDataRef SOSPeerInfoGetAutoAcceptInfo(SOSPeerInfoRef peer) {
CFDataRef pubKeyBytes = NULL;
pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
if (!pubKeyBytes || CFGetTypeID(pubKeyBytes) != CFDataGetTypeID()) {
pubKeyBytes = NULL;
}
return pubKeyBytes;
}
static bool SOSDescriptionHash(SOSPeerInfoRef peer, const struct ccdigest_info *di, void *hashresult, CFErrorRef *error) {
ccdigest_di_decl(di, ctx);
ccdigest_init(di, ctx);
void *ctx_p = ctx;
if(!SOSPeerInfoUpdateDigestWithDescription(peer, di, ctx_p, error)) return false;
ccdigest_final(di, ctx, hashresult);
return true;
}
#define SIGLEN 128
static CFDataRef sosCopySignedHash(SecKeyRef privkey, const struct ccdigest_info *di, uint8_t *hbuf) {
OSStatus stat;
size_t siglen = SIGLEN;
uint8_t sig[siglen];
if((stat = SecKeyRawSign(privkey, kSecPaddingNone, hbuf, di->output_size, sig, &siglen)) != 0) {
return NULL;
}
return CFDataCreate(NULL, sig, (CFIndex)siglen);
}
static bool sosVerifyHash(SecKeyRef pubkey, const struct ccdigest_info *di, uint8_t *hbuf, CFDataRef signature) {
return SecKeyRawVerify(pubkey, kSecPaddingNone, hbuf, di->output_size,
CFDataGetBytePtr(signature), CFDataGetLength(signature)) == errSecSuccess;
}
bool SOSPeerInfoSign(SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
bool status = false;
const struct ccdigest_info *di = ccsha256_di();
uint8_t hbuf[di->output_size];
CFDataRef newSignature = NULL;
require_action_quiet(SOSDescriptionHash(peer, di, hbuf, error), fail,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to hash description for peer"), NULL, error));
newSignature = sosCopySignedHash(privKey, di, hbuf);
require_action_quiet(newSignature, fail, SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign peerinfo for peer"), NULL, error));
CFReleaseNull(peer->signature);
peer->signature = newSignature;
newSignature = NULL;
status = true;
fail:
CFReleaseNull(newSignature);
return status;
}
// Return true (1) if the signature verifies.
bool SOSPeerInfoVerify(SOSPeerInfoRef peer, CFErrorRef *error) {
bool result = false;
const struct ccdigest_info *di = ccsha256_di();
uint8_t hbuf[di->output_size];
SecKeyRef pubKey = SOSPeerInfoCopyPubKey(peer, error);
require_quiet(pubKey, error_out);
require_quiet(SOSDescriptionHash(peer, di, hbuf, error), error_out);
require_action_quiet(sosVerifyHash(pubKey, di, hbuf, peer->signature), error_out,
SOSErrorCreate(kSOSErrorBadSignature, error, NULL,
CFSTR("Signature didn't verify for result = true;
error_out:
CFReleaseNull(pubKey);
return result;
}
void SOSPeerInfoSetVersionNumber(SOSPeerInfoRef pi, int version) {
pi->version = version;
CFNumberRef versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
CFDictionarySetValue(pi->description, sVersionKey, versionNumber);
CFReleaseNull(versionNumber);
}
static SOSPeerInfoRef SOSPeerInfoCreate_Internal(CFAllocatorRef allocator,
CFDictionaryRef gestalt, CFDataRef backup_key,
CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel,
CFSetRef enabledViews,
SecKeyRef signingKey,
SecKeyRef octagonPeerSigningKey,
SecKeyRef octagonPeerEncryptionKey,
CFErrorRef* error,
void (^ description_modifier)(CFMutableDictionaryRef description)) {
SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
pi->gestalt = gestalt;
CFRetain(pi->gestalt);
pi->version = SOSPeerInfoGetPeerProtocolVersion(pi);
CFDataRef publicBytes = NULL;
CFDataRef octagonPeerSigningPublicBytes = NULL;
CFDataRef octagonPeerEncryptionPublicBytes = NULL;
CFNumberRef versionNumber = NULL;
SecKeyRef publicKey = SecKeyCreatePublicFromPrivate(signingKey);
if (publicKey == NULL) {
SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public"), NULL, error);
CFReleaseNull(pi);
goto exit;
}
OSStatus result = SecKeyCopyPublicBytes(publicKey, &publicBytes);
if (result != errSecSuccess) {
SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
CFReleaseNull(pi);
goto exit;
}
if (octagonPeerSigningKey) {
SecKeyRef octagonPeerSigningPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerSigningKey);
if (octagonPeerSigningPublicKey == NULL) {
SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
CFReleaseNull(pi);
goto exit;
}
result = SecKeyCopyPublicBytes(octagonPeerSigningPublicKey, &octagonPeerSigningPublicBytes);
CFReleaseNull(octagonPeerSigningPublicKey);
if (result != errSecSuccess) {
SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
CFReleaseNull(pi);
goto exit;
}
}
if (octagonPeerEncryptionKey) {
SecKeyRef octagonPeerEncryptionPublicKey = SecKeyCreatePublicFromPrivate(octagonPeerEncryptionKey);
if (octagonPeerEncryptionPublicKey == NULL) {
SOSCreateError(kSOSErrorBadKey, CFSTR("Unable to get public key"), NULL, error);
CFReleaseNull(pi);
goto exit;
}
result = SecKeyCopyPublicBytes(octagonPeerEncryptionPublicKey, &octagonPeerEncryptionPublicBytes);
CFReleaseNull(octagonPeerEncryptionPublicKey);
if (result != errSecSuccess) {
SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
CFReleaseNull(pi);
goto exit;
}
}
pi->signature = CFDataCreateMutable(allocator, 0);
versionNumber = CFNumberCreateWithCFIndex(NULL, pi->version);
pi->description = CFDictionaryCreateMutableForCFTypesWith(allocator,
sVersionKey, versionNumber,
sPublicKeyKey, publicBytes,
sGestaltKey, pi->gestalt,
NULL);
if (octagonPeerSigningPublicBytes) {
CFDictionarySetValue(pi->description, sOctagonPeerSigningPublicKeyKey, octagonPeerSigningPublicBytes);
}
if (octagonPeerEncryptionPublicBytes) {
CFDictionarySetValue(pi->description, sOctagonPeerEncryptionPublicKeyKey, octagonPeerEncryptionPublicBytes);
}
description_modifier(pi->description);
pi->peerID = SOSCopyIDOfKey(publicKey, error);
pi->verifiedAppKeyID = NULL;
pi->verifiedResult = false;
require_quiet(pi->peerID, exit);
// ================ V2 Additions Start
if(!SOSPeerInfoUpdateToV2(pi, error)) {
CFReleaseNull(pi);
goto exit;
}
// V2DictionarySetValue handles NULL as remove
if (backup_key != NULL) SOSPeerInfoV2DictionarySetValue(pi, sBackupKeyKey, backup_key);
SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews);
// ================ V2 Additions End
if (!SOSPeerInfoSign(signingKey, pi, error)) {
CFReleaseNull(pi);
goto exit;
}
exit:
CFReleaseNull(versionNumber);
CFReleaseNull(publicKey);
CFReleaseNull(publicBytes);
CFReleaseNull(octagonPeerSigningPublicBytes);
CFReleaseNull(octagonPeerEncryptionPublicBytes);
return pi;
}
SOSPeerInfoRef SOSPeerInfoCreate(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key, SecKeyRef signingKey, SecKeyRef octagonPeerSigningKey, SecKeyRef octagonPeerEncryptionKey, CFErrorRef* error) {
return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error, ^(CFMutableDictionaryRef description) {});
}
SOSPeerInfoRef SOSPeerInfoCreateWithTransportAndViews(CFAllocatorRef allocator, CFDictionaryRef gestalt, CFDataRef backup_key,
CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS,
CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews,
SecKeyRef signingKey,
SecKeyRef octagonPeerSigningKey,
SecKeyRef octagonPeerEncryptionKey,
CFErrorRef* error)
{
return SOSPeerInfoCreate_Internal(allocator, gestalt, backup_key, IDSID, transportType, preferIDS, preferFragmentation, preferAckModel, enabledViews, signingKey, octagonPeerSigningKey, octagonPeerEncryptionKey, error, ^(CFMutableDictionaryRef description) {});
}
SOSPeerInfoRef SOSPeerInfoCreateCloudIdentity(CFAllocatorRef allocator, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
return SOSPeerInfoCreate_Internal(allocator, gestalt, NULL, NULL, NULL, NULL, NULL, NULL, NULL, signingKey, NULL, NULL, error, ^(CFMutableDictionaryRef description) {
CFDictionarySetValue(description, sCloudIdentityKey, kCFBooleanTrue);
});
}
SOSPeerInfoRef SOSPeerInfoCreateCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFErrorRef* error) {
if(!toCopy) return NULL;
SOSPeerInfoRef pi = CFTypeAllocate(SOSPeerInfo, struct __OpaqueSOSPeerInfo, allocator);
pi->description = CFDictionaryCreateMutableCopy(allocator, 0, toCopy->description);
pi->signature = CFDataCreateCopy(allocator, toCopy->signature);
pi->gestalt = CFDictionaryCreateCopy(allocator, toCopy->gestalt);
pi->peerID = CFStringCreateCopy(allocator, toCopy->peerID);
pi->verifiedAppKeyID = NULL; // The peer resulting from this will need to be re-evaluated for an application signature.
pi->verifiedResult = false;
pi->version = toCopy->version;
if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoExpandV2Data(pi, error);
return pi;
}
bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi) {
return pi->version >= PEERINFO_CURRENT_VERSION;
}
bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi) {
return pi->version >= kSOSPeerV2BaseVersion;
}
SOSPeerInfoRef SOSPeerInfoCreateCurrentCopy(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
CFStringRef IDSID, CFStringRef transportType, CFBooleanRef preferIDS, CFBooleanRef preferFragmentation, CFBooleanRef preferAckModel, CFSetRef enabledViews,
SecKeyRef signingKey, CFErrorRef* error) {
SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
if(!SOSPeerInfoVersionHasV2Data(pi)) SOSPeerInfoUpdateToV2(pi, error);
if (enabledViews) {
SOSPeerInfoV2DictionarySetValue(pi, sViewsKey, enabledViews);
}
if(!SOSPeerInfoSign(signingKey, pi, error)) {
CFReleaseNull(pi);
}
return pi;
}
static SOSPeerInfoRef SOSPeerInfoCopyWithModification(CFAllocatorRef allocator, SOSPeerInfoRef original,
SecKeyRef signingKey, CFErrorRef *error,
bool (^modification)(SOSPeerInfoRef peerToModify, CFErrorRef *error)) {
SOSPeerInfoRef result = NULL;
SOSPeerInfoRef copy = SOSPeerInfoCreateCopy(allocator, original, error);
require_quiet(modification(copy, error), fail);
require_quiet(SOSPeerInfoSign(signingKey, copy, error), fail);
CFTransferRetained(result, copy);
fail:
CFReleaseNull(copy);
return result;
}
SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error) {
return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
if(!gestalt || !peerToModify) return false;
CFRetainAssign(peerToModify->gestalt, gestalt);
CFDictionarySetValue(peerToModify->description, sGestaltKey, peerToModify->gestalt);
return true;
});
}
SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef *error) {
return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
if (backupKey != NULL)
SOSPeerInfoV2DictionarySetValue(peerToModify, sBackupKeyKey, backupKey);
else
SOSPeerInfoV2DictionaryRemoveValue(peerToModify, sBackupKeyKey);
return true;
});
}
static CFDictionaryRef SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord){
CFMutableDictionaryRef existingEscrowRecords = SOSPeerInfoCopyEscrowRecord(peer);
if(escrowRecord == NULL && existingEscrowRecords != NULL)
{
CFDictionaryRemoveValue(existingEscrowRecords, dsid);
return existingEscrowRecords;
}
if(existingEscrowRecords == NULL)
existingEscrowRecords = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
CFDictionarySetValue(existingEscrowRecords, dsid, escrowRecord);
return existingEscrowRecords;
}
SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error) {
return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
CFDictionaryRef updatedEscrowRecords = SOSPeerInfoUpdateAndCopyRecord(peerToModify, dsid, escrowRecord);
SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, updatedEscrowRecords);
CFReleaseNull(updatedEscrowRecords);
return true;
});
}
SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) {
return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
if(escrowRecords != NULL)
SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, escrowRecords);
return true;
});
}
CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer) {
return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey);
}
CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer){
return SOSPeerInfoV2DictionaryCopyDictionary(peer, sEscrowRecord);
}
bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) {
CFDataRef bk = SOSPeerInfoCopyBackupKey(peer);
bool success = bk != NULL;
CFReleaseNull(bk);
return success;
}
SOSPeerInfoRef SOSPeerInfoCopyWithViewsChange(CFAllocatorRef allocator, SOSPeerInfoRef toCopy,
SOSViewActionCode action, CFStringRef viewname, SOSViewResultCode *retval,
SecKeyRef signingKey, CFErrorRef* error) {
SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
if(action == kSOSCCViewEnable) {
*retval = SOSViewsEnable(pi, viewname, error);
require((kSOSCCViewMember == *retval), exit);
} else if(action == kSOSCCViewDisable) {
*retval = SOSViewsDisable(pi, viewname, error);
require((kSOSCCViewNotMember == *retval), exit);
}
require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, *retval = kSOSCCGeneralViewError);
return pi;
exit:
CFReleaseNull(pi);
return NULL;
}
CFStringRef sPingKey = CFSTR("Ping");
SOSPeerInfoRef SOSPeerInfoCopyWithPing(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, SecKeyRef signingKey, CFErrorRef* error) {
SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, toCopy, error);
CFDataRef ping = CFDataCreateWithRandomBytes(8);
SOSPeerInfoV2DictionarySetValue(pi, sPingKey, ping);
SecKeyRef pub_key = SOSPeerInfoCopyPubKey(pi, error);
require_quiet(pub_key, exit);
pi->peerID = SOSCopyIDOfKey(pub_key, error);
require_quiet(pi->peerID, exit);
require_action_quiet(SOSPeerInfoSign(signingKey, pi, error), exit, CFReleaseNull(pi));
exit:
CFReleaseNull(ping);
CFReleaseNull(pub_key);
return pi;
}
SOSViewResultCode SOSPeerInfoViewStatus(SOSPeerInfoRef pi, CFStringRef view, CFErrorRef *error) {
return SOSViewsQuery(pi, view, error);
}
static void SOSPeerInfoDestroy(CFTypeRef aObj) {
SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
if(!pi) return;
CFReleaseNull(pi->description);
CFReleaseNull(pi->signature);
CFReleaseNull(pi->gestalt);
CFReleaseNull(pi->peerID);
CFReleaseNull(pi->v2Dictionary);
CFReleaseNull(pi->verifiedAppKeyID);
pi->verifiedResult = false;
}
static Boolean SOSPeerInfoCompare(CFTypeRef lhs, CFTypeRef rhs) {
SOSPeerInfoRef lpeer = (SOSPeerInfoRef) lhs;
SOSPeerInfoRef rpeer = (SOSPeerInfoRef) rhs;
if(!lpeer || !rpeer) return false;
return CFEqualSafe(lpeer->description, rpeer->description) && CFEqualSafe(lpeer->signature, rpeer->signature);
}
CFComparisonResult SOSPeerInfoCompareByID(const void *val1, const void *val2, void *context) {
// The code below is necessary but not sufficient; not returning a CFComparisonResult
// It probably is OK to say that a NULL is < <non-NULL>
if (val1 == NULL || val2 == NULL) {
ptrdiff_t dv = val1 - val2;
return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
}
CFStringRef v1 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val1);
CFStringRef v2 = SOSPeerInfoGetPeerID((SOSPeerInfoRef) val2);
if (v1 == NULL || v2 == NULL) {
ptrdiff_t dv = (const void *)v1 - (const void *)v2;
return dv < 0 ? kCFCompareLessThan : dv == 0 ? kCFCompareEqualTo : kCFCompareGreaterThan;
}
return CFStringCompare(v1, v2, 0);
}
static CFHashCode SOSPeerInfoHash(CFTypeRef cf) {
SOSPeerInfoRef peer = (SOSPeerInfoRef) cf;
return CFHash(peer->description) ^ CFHash(peer->signature);
}
static char boolToChars(bool val, char truechar, char falsechar) {
return val? truechar: falsechar;
}
static CFStringRef isKnown(CFStringRef ref) {
return ref? ref: CFSTR("Unknown ");
}
static CFStringRef copyDescriptionWithFormatOptions(CFTypeRef aObj, CFDictionaryRef formatOptions){
SOSPeerInfoRef pi = (SOSPeerInfoRef) aObj;
if(!pi) return NULL;
CFStringRef description = NULL;
// Get the format options we care about:
bool retired = SOSPeerInfoIsRetirementTicket(pi);
bool selfValid = SOSPeerInfoVerify(pi, NULL);
bool backingUp = SOSPeerInfoHasBackupKey(pi);
bool isKVS = SOSPeerInfoKVSOnly(pi);
CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey);
CFStringRef tmp = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID);
CFStringRef deviceID = CFStringCreateTruncatedCopy(tmp, 8);
CFReleaseNull(tmp);
CFStringRef serialNum = SOSPeerInfoCopySerialNumber(pi);
CFStringRef peerID = CFStringCreateTruncatedCopy(SOSPeerInfoGetPeerID(pi), 8);
// Calculate the truncated length
CFStringRef objectPrefix = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("PI@
description = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions,
CFSTR("< objectPrefix,
isKnown(SOSPeerInfoGetPeerName(pi)),
'-',
'-',
boolToChars(selfValid, 'S', 's'),
boolToChars(retired, 'R', 'r'),
boolToChars(backingUp, 'B', 'b'),
boolToChars(isKVS, 'K', 'I'),
'-',
isKnown(SOSPeerInfoGetPeerDeviceType(pi)), isKnown(peerID),
isKnown(osVersion), isKnown(deviceID), isKnown(serialNum));
CFReleaseNull(peerID);
CFReleaseNull(deviceID);
CFReleaseNull(serialNum);
CFReleaseNull(objectPrefix);
return description;
}
static CFStringRef SOSPeerInfoCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
CFStringRef description = NULL;
description = copyDescriptionWithFormatOptions(aObj, formatOptions);
return description;
}
void SOSPeerInfoLogState(char *category, SOSPeerInfoRef pi, SecKeyRef pubKey, CFStringRef myPID, char sigchr) {
if(!pi) return;
bool appValid = SOSPeerInfoApplicationVerify(pi, pubKey, NULL);
bool retired = SOSPeerInfoIsRetirementTicket(pi);
// We won't inflate invalid peerInfos. Mark this true to keep scanning utilities from breaking.
bool selfValid = true;
bool backingUp = SOSPeerInfoHasBackupKey(pi);
bool isMe = CFEqualSafe(SOSPeerInfoGetPeerID(pi), myPID) == true;
bool isKVS = SOSPeerInfoKVSOnly(pi);
CFStringRef osVersion = CFDictionaryGetValue(pi->gestalt, kPIOSVersionKey);
CFStringRef tmp = SOSPeerInfoV2DictionaryCopyString(pi, sDeviceID);
CFStringRef deviceID = CFStringCreateTruncatedCopy(tmp, 8);
CFReleaseNull(tmp);
CFStringRef serialNum = SOSPeerInfoCopySerialNumber(pi);
CFStringRef peerID = CFStringCreateTruncatedCopy(SOSPeerInfoGetPeerID(pi), 8);
secnotice(category, "PI: [name: boolToChars(isMe, 'M', 'm'),
boolToChars(appValid, 'A', 'a'),
boolToChars(selfValid, 'S', 's'),
boolToChars(retired, 'R', 'r'),
boolToChars(backingUp, 'B', 'b'),
boolToChars(isKVS, 'K', 'I'),
sigchr,
isKnown(SOSPeerInfoGetPeerDeviceType(pi)), isKnown(peerID),
isKnown(osVersion), isKnown(deviceID), isKnown(serialNum));
CFReleaseNull(peerID);
CFReleaseNull(deviceID);
CFReleaseNull(serialNum);
}
CFDictionaryRef SOSPeerInfoCopyPeerGestalt(SOSPeerInfoRef pi) {
CFRetain(pi->gestalt);
return pi->gestalt;
}
CFDictionaryRef SOSPeerGetGestalt(SOSPeerInfoRef pi){
return pi->gestalt;
}
CFStringRef SOSPeerInfoGetPeerName(SOSPeerInfoRef peer) {
return SOSPeerInfoLookupGestaltValue(peer, kPIUserDefinedDeviceNameKey);
}
CFStringRef SOSPeerInfoGetPeerDeviceType(SOSPeerInfoRef peer) {
return SOSPeerInfoLookupGestaltValue(peer, kPIDeviceModelNameKey);
}
CFIndex SOSPeerInfoGetPeerProtocolVersion(SOSPeerInfoRef peer) {
CFIndex version = PEERINFO_CURRENT_VERSION;
CFTypeRef val = SOSPeerInfoLookupGestaltValue(peer, kPIMessageProtocolVersionKey);
if (val && CFGetTypeID(val) == CFNumberGetTypeID())
CFNumberGetValue(val, kCFNumberCFIndexType, &version);
return version;
}
CFTypeRef SOSPeerInfoLookupGestaltValue(SOSPeerInfoRef pi, CFStringRef key) {
return CFDictionaryGetValue(pi->gestalt, key);
}
CFStringRef SOSPeerInfoGetPeerID(SOSPeerInfoRef pi) {
return pi ? pi->peerID : NULL;
}
bool SOSPeerInfoPeerIDEqual(SOSPeerInfoRef pi, CFStringRef myPeerID) {
return CFEqualSafe(myPeerID, SOSPeerInfoGetPeerID(pi));
}
CFIndex SOSPeerInfoGetVersion(SOSPeerInfoRef pi) {
return pi->version;
}
bool SOSPeerInfoUpdateDigestWithPublicKeyBytes(SOSPeerInfoRef peer, const struct ccdigest_info *di,
ccdigest_ctx_t ctx, CFErrorRef *error) {
CFDataRef pubKeyBytes = CFDictionaryGetValue(peer->description, sPublicKeyKey);
if(!pubKeyBytes) {
SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Digest failed – no public key"));
return false;
}
ccdigest_update(di, ctx, CFDataGetLength(pubKeyBytes), CFDataGetBytePtr(pubKeyBytes));
return true;
}
bool SOSPeerInfoUpdateDigestWithDescription(SOSPeerInfoRef peer, const struct ccdigest_info *di,
ccdigest_ctx_t ctx, CFErrorRef *error) {
if(SOSPeerInfoVersionHasV2Data(peer)) SOSPeerInfoPackV2Data(peer);
size_t description_size = der_sizeof_plist(peer->description, error);
if (description_size == 0) {
SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description length failed"));
return false;
}
uint8_t * data = malloc(description_size);
if (data == NULL) {
SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description alloc failed"));
return false;
}
uint8_t *data_end = data + description_size;
uint8_t *encoded = der_encode_plist(peer->description, error, data, data_end);
if(!encoded) {
free(data);
SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, NULL, CFSTR("Description encode failed"));
return false;
}
ccdigest_update(di, ctx, description_size, data);
free(data);
return true;
}
static CFDataRef sosCreateDate() {
CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
size_t bufsiz = der_sizeof_date(now, NULL);
uint8_t buf[bufsiz];
der_encode_date(now, NULL, buf, buf+bufsiz);
CFReleaseNull(now);
return CFDataCreate(NULL, buf, bufsiz);
}
static CFDateRef sosCreateCFDate(CFDataRef sosdate) {
CFDateRef date;
der_decode_date(NULL, 0, &date, NULL, CFDataGetBytePtr(sosdate),
CFDataGetBytePtr(sosdate) + CFDataGetLength(sosdate));
return date;
}
static bool sospeer_application_hash(SOSPeerInfoRef pi, const struct ccdigest_info *di, uint8_t *hbuf) {
CFDataRef appdate = asData(CFDictionaryGetValue(pi->description, sApplicationDate), NULL);
if(!appdate) return false;
ccdigest_di_decl(di, ctx);
ccdigest_init(di, ctx);
ccdigest_update(di, ctx, CFDataGetLength(appdate), CFDataGetBytePtr(appdate));
if (!SOSPeerInfoUpdateDigestWithPublicKeyBytes(pi, di, ctx, NULL)) return false;
ccdigest_final(di, ctx, hbuf);
return true;
}
SOSPeerInfoRef SOSPeerInfoCopyAsApplication(SOSPeerInfoRef original, SecKeyRef userkey, SecKeyRef peerkey, CFErrorRef *error) {
SOSPeerInfoRef result = NULL;
SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, original, error);
const struct ccdigest_info *di = ccsha256_di();
uint8_t hbuf[di->output_size];
CFDataRef usersig = NULL;
CFDataRef creationDate = sosCreateDate();
CFDictionarySetValue(pi->description, sApplicationDate, creationDate);
CFReleaseNull(creationDate);
// Create User Application Signature
require_action_quiet(sospeer_application_hash(pi, di, hbuf), fail,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
usersig = sosCopySignedHash(userkey, di, hbuf);
require_action_quiet(usersig, fail,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to sign public key hash for peer"), NULL, error));
CFDictionarySetValue(pi->description, sApplicationUsig, usersig);
pi->verifiedAppKeyID = SOSCopyIDOfKey(userkey, error);
if(pi->verifiedAppKeyID == NULL) {
secnotice("PICache", "failed to get userKeyID");
} else {
pi->verifiedResult = true;
}
require_quiet(SOSPeerInfoSign(peerkey, pi, error), fail);
result = pi;
pi = NULL;
fail:
CFReleaseNull(usersig);
CFReleaseNull(pi);
return result;
}
bool SOSPeerInfoApplicationVerify(SOSPeerInfoRef pi, SecKeyRef userkey, CFErrorRef *error) {
const struct ccdigest_info *di = ccsha256_di();
uint8_t hbuf[di->output_size];
bool result = false;
CFStringRef userKeyID = NULL;
// If we've already succeeded signature check with this key move on.
require_action_quiet(userkey, exit, SOSErrorCreate(kSOSErrorNoKey, error, NULL, CFSTR("Can't validate PeerInfos with no userKey")));
userKeyID = SOSCopyIDOfKey(userkey, error);
require_action_quiet(!CFEqualSafe(userKeyID, pi->verifiedAppKeyID), exit, result = pi->verifiedResult);
// verifiedAppKeyID was NULL or not the key we're looking for - clear it.
CFReleaseNull(pi->verifiedAppKeyID);
pi->verifiedResult = false;
CFDataRef usig = CFDictionaryGetValue(pi->description, sApplicationUsig);
require_action_quiet(usig, exit,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not an applicant"), NULL, error));
// Verify User Application Signature
require_action_quiet(sospeer_application_hash(pi, di, hbuf), exit,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Failed to create hash for peer applicant"), NULL, error));
require_action_quiet(sosVerifyHash(userkey, di, hbuf, usig), exit,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("user signature of public key hash fails to verify"), NULL, error));
// Remember the userkey we validated for this peerinfo.
pi->verifiedAppKeyID = CFStringCreateCopy(kCFAllocatorDefault, userKeyID);
pi->verifiedResult = true;
result = SOSPeerInfoVerify(pi, error);
exit:
CFReleaseNull(userKeyID);
return result;
}
static CF_RETURNS_RETAINED CFDateRef sosPeerInfoGetDate(SOSPeerInfoRef pi, CFStringRef entry) {
if(!pi) return NULL;
CFDataRef sosdate = CFDictionaryGetValue(pi->description, entry);
if(!sosdate) return NULL;
CFDateRef date = sosCreateCFDate(sosdate);
return date;
}
CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi) {
return sosPeerInfoGetDate(pi, sApplicationDate);
}
CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetRetirementDate(SOSPeerInfoRef pi) {
return sosPeerInfoGetDate(pi, sRetirementDate);
}
//
// Gestalt helpers
//
CFStringRef SOSPeerGestaltGetName(CFDictionaryRef gestalt) {
CFStringRef name = SOSPeerGestaltGetAnswer(gestalt, kPIUserDefinedDeviceNameKey);
return isString(name) ? name : NULL;
}
CFTypeRef SOSPeerGestaltGetAnswer(CFDictionaryRef gestalt, CFStringRef question) {
return gestalt ? CFDictionaryGetValue(gestalt, question) : NULL;
}
//
// Peer Retirement
//
SOSPeerInfoRef SOSPeerInfoCreateRetirementTicket(CFAllocatorRef allocator, SecKeyRef privKey, SOSPeerInfoRef peer, CFErrorRef *error) {
// Copy PeerInfo
SOSPeerInfoRef pi = SOSPeerInfoCreateCopy(allocator, peer, error);
require(pi, fail);
// Fill out Resignation Date
CFDataRef resignationDate = sosCreateDate();
CFDictionaryAddValue(pi->description, sRetirementDate, resignationDate);
CFReleaseNull(resignationDate);
require(SOSPeerInfoSign(privKey, pi, error), fail);
return pi;
fail:
CFReleaseNull(pi);
return NULL;
}
CFStringRef SOSPeerInfoInspectRetirementTicket(SOSPeerInfoRef pi, CFErrorRef *error) {
CFStringRef retval = NULL;
CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
CFDateRef retirement = NULL;
require_quiet(SOSPeerInfoVerify(pi, error), err);
retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
require_action_quiet(retirement, err,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Peer is not retired"), NULL, error));
require_action_quiet(CFDateCompare(now, retirement, NULL) == kCFCompareGreaterThan, err,
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Retirement date is after current date"), NULL, error));
retval = SOSPeerInfoGetPeerID(pi);
err:
CFReleaseNull(now);
CFReleaseNull(retirement);
return retval;
}
bool SOSPeerInfoRetireRetirementTicket(size_t max_seconds, SOSPeerInfoRef pi) {
CFDateRef now = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
CFDateRef retirement = sosCreateCFDate(CFDictionaryGetValue(pi->description, sRetirementDate));
CFTimeInterval timediff = CFDateGetTimeIntervalSinceDate(now, retirement); // diff in seconds
CFReleaseNull(now);
CFReleaseNull(retirement);
if(timediff > (max_seconds)) return true;
return false;
}
bool SOSPeerInfoIsRetirementTicket(SOSPeerInfoRef pi) {
CFDataRef flag = CFDictionaryGetValue(pi->description, sRetirementDate);
return flag != NULL;
}
bool SOSPeerInfoIsCloudIdentity(SOSPeerInfoRef pi) {
CFTypeRef value = CFDictionaryGetValue(pi->description, sCloudIdentityKey);
return CFEqualSafe(value, kCFBooleanTrue);
}
SOSPeerInfoRef SOSPeerInfoUpgradeSignatures(CFAllocatorRef allocator, SecKeyRef privKey, SecKeyRef peerKey, SOSPeerInfoRef peer, CFErrorRef *error) {
SecKeyRef pubKey = SecKeyCreatePublicFromPrivate(privKey);
SOSPeerInfoRef retval = NULL;
retval = SOSPeerInfoCopyAsApplication(peer, privKey, peerKey, error);
CFReleaseNull(pubKey);
return retval;
}
static SOSPeerInfoRef CF_RETURNS_RETAINED
SOSPeerInfoSetOctagonKey(CFAllocatorRef allocator,
SOSPeerInfoRef toCopy,
CFStringRef descriptionKey,
SecKeyRef octagonKey,
SecKeyRef signingKey,
CFErrorRef *error)
{
CFDataRef publicKeyBytes = NULL;
SOSPeerInfoRef pi = NULL;
OSStatus copyResult = SecKeyCopyPublicBytes(octagonKey, &publicKeyBytes);
require_action_quiet(0 == copyResult, fail, SecError(copyResult, error, CFSTR("failed to copy public key bytes")));
pi = SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
if(peerToModify && peerToModify->description && publicKeyBytes && descriptionKey) {
CFDictionarySetValue(peerToModify->description, descriptionKey, publicKeyBytes);
} else {
SecError(errSecParam, error, CFSTR("Bad key bytes or dictionary key"));
}
return true;
});
require(pi, fail);
fail:
CFReleaseNull(publicKeyBytes);
return pi;
}
SOSPeerInfoRef CF_RETURNS_RETAINED
SOSPeerInfoSetOctagonSigningKey(CFAllocatorRef allocator,
SOSPeerInfoRef toCopy,
SecKeyRef octagonSigningKey,
SecKeyRef signingKey,
CFErrorRef *error)
{
return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerSigningPublicKeyKey, octagonSigningKey, signingKey, error);
}
SOSPeerInfoRef CF_RETURNS_RETAINED
SOSPeerInfoSetOctagonEncryptionKey(CFAllocatorRef allocator,
SOSPeerInfoRef toCopy,
SecKeyRef octagonEncryptionKey,
SecKeyRef signingKey,
CFErrorRef *error)
{
return SOSPeerInfoSetOctagonKey(allocator, toCopy, sOctagonPeerEncryptionPublicKeyKey, octagonEncryptionKey, signingKey, error);
}
CFStringRef SOSPeerInfoCopyTransportType(SOSPeerInfoRef peer){
CFStringRef transportType = (CFStringRef)SOSPeerInfoV2DictionaryCopyString(peer, sTransportType);
return (transportType ? transportType : CFRetain(SOSTransportMessageTypeKVS));
}
bool SOSPeerInfoKVSOnly(SOSPeerInfoRef pi) {
CFStringRef transportType = SOSPeerInfoCopyTransportType(pi);
bool retval = CFEqualSafe(transportType, SOSTransportMessageTypeKVS);
CFReleaseNull(transportType);
return retval;
}
CFStringRef SOSPeerInfoCopyDeviceID(SOSPeerInfoRef peer){
return CFSTR("not implemented");
}
SOSPeerInfoDeviceClass SOSPeerInfoGetClass(SOSPeerInfoRef pi) {
static CFDictionaryRef devID2Class = NULL;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
CFNumberRef cfSOSPeerInfo_macOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_macOS);
CFNumberRef cfSOSPeerInfo_iOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_iOS);
CFNumberRef cfSOSPeerInfo_iCloud = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_iCloud);
// CFNumberRef cfSOSPeerInfo_watchOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_watchOS);
// CFNumberRef cfSOSPeerInfo_tvOS = CFNumberCreateWithCFIndex(kCFAllocatorDefault, SOSPeerInfo_tvOS);
devID2Class = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
CFSTR("Mac Pro"), cfSOSPeerInfo_macOS,
CFSTR("MacBook"), cfSOSPeerInfo_macOS,
CFSTR("MacBook Pro"), cfSOSPeerInfo_macOS,
CFSTR("iCloud"), cfSOSPeerInfo_iCloud,
CFSTR("iMac"), cfSOSPeerInfo_macOS,
CFSTR("iPad"), cfSOSPeerInfo_iOS,
CFSTR("iPhone"), cfSOSPeerInfo_iOS,
CFSTR("iPod touch"), cfSOSPeerInfo_iOS,
NULL);
CFReleaseNull(cfSOSPeerInfo_macOS);
CFReleaseNull(cfSOSPeerInfo_iOS);
CFReleaseNull(cfSOSPeerInfo_iCloud);
});
SOSPeerInfoDeviceClass retval = SOSPeerInfo_unknown;
CFStringRef dt = SOSPeerInfoGetPeerDeviceType(pi);
require_quiet(dt, errOut);
CFNumberRef classNum = CFDictionaryGetValue(devID2Class, dt);
require_quiet(classNum, errOut);
CFIndex tmp;
require_quiet(CFNumberGetValue(classNum, kCFNumberCFIndexType, &tmp), errOut);
retval = (SOSPeerInfoDeviceClass) tmp;
errOut:
return retval;
}
bool SOSPeerInfoHasOctagonSigningPubKey(SOSPeerInfoRef peer){
bool hasKey = false;
SecKeyRef pubKey = SOSPeerInfoCopyOctagonSigningPublicKey(peer, NULL);
if(pubKey)
hasKey = true;
CFReleaseNull(pubKey);
return hasKey;
}
bool SOSPeerInfoHasOctagonEncryptionPubKey(SOSPeerInfoRef peer){
bool hasKey = false;
SecKeyRef pubKey = SOSPeerInfoCopyOctagonEncryptionPublicKey(peer, NULL);
if(pubKey)
hasKey = true;
CFReleaseNull(pubKey);
return hasKey;
}