SOSPeerInfoCollections.c [plain text]
#include "keychain/SecureObjectSync/SOSPeerInfoCollections.h"
#include <CoreFoundation/CoreFoundation.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecCFError.h>
#include <utilities/SecXPCError.h>
#include <corecrypto/ccder.h>
#include "keychain/SecureObjectSync/SOSInternal.h"
#include <AssertMacros.h>
#include "keychain/SecureObjectSync/SOSPeerInfoDER.h"
static CFStringRef GetIDOrString(const void *object) {
return isSOSPeerInfo(object) ?
SOSPeerInfoGetPeerID((SOSPeerInfoRef)object) :
(isString(object) ? (CFStringRef) object : NULL);
}
static Boolean SOSPeerInfoIDEqual(const void *value1, const void *value2)
{
return CFEqualSafe(GetIDOrString(value1), GetIDOrString(value2));
}
static CFHashCode SOSPeerInfoIDHash(const void *value)
{
return CFHash(GetIDOrString(value));
}
bool SOSPeerInfoSetContainsIdenticalPeers(CFSetRef set1, CFSetRef set2){
__block bool result = true;
if(!CFEqualSafe(set1, set2))
return false;
CFSetForEach(set1, ^(const void *value) {
SOSPeerInfoRef peer1 = (SOSPeerInfoRef)value;
SOSPeerInfoRef peer2 = (SOSPeerInfoRef)CFSetGetValue(set2, peer1);
result &= CFEqualSafe(peer1, peer2);
});
return result;
}
const CFSetCallBacks kSOSPeerSetCallbacks = {0, SecCFRetainForCollection, SecCFReleaseForCollection, CFCopyDescription, SOSPeerInfoIDEqual, SOSPeerInfoIDHash};
CFMutableSetRef CFSetCreateMutableForSOSPeerInfosByID(CFAllocatorRef allocator)
{
return CFSetCreateMutable(allocator, 0, &kSOSPeerSetCallbacks);
}
CFMutableSetRef CFSetCreateMutableForSOSPeerInfosByIDWithArray(CFAllocatorRef allocator, CFArrayRef peerInfos)
{
CFMutableSetRef newSet = CFSetCreateMutableForSOSPeerInfosByID(allocator);
CFArrayForEach(peerInfos, ^(const void *value) {
SOSPeerInfoRef peer = asSOSPeerInfo(value);
if (peer) {
CFSetAddValue(newSet, peer);
}
});
return newSet;
}
SOSPeerInfoRef SOSPeerInfoSetFindByID(CFSetRef set, CFStringRef id) {
return (SOSPeerInfoRef) CFSetGetValue(set, id);
}
void CFArrayOfSOSPeerInfosSortByID(CFMutableArrayRef peerInfoArray)
{
if(peerInfoArray && CFArrayGetCount(peerInfoArray) > 0) {
CFArraySortValues(peerInfoArray, CFRangeMake(0, CFArrayGetCount(peerInfoArray)), SOSPeerInfoCompareByID, NULL);
}
}
CFMutableArrayRef SOSPeerInfoArrayCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end) {
CFMutableArrayRef pia = CFArrayCreateMutableForCFTypes(allocator);
const uint8_t *sequence_end;
*der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
require_action(*der_p, fail, SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array Sequence Header"), NULL, error));
while (sequence_end != *der_p) {
SOSPeerInfoRef pi = SOSPeerInfoCreateFromDER(allocator, error, der_p, sequence_end);
if (pi == NULL || *der_p == NULL) {
SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Peer Info Array DER"), (error != NULL ? *error : NULL), error);
CFReleaseNull(pi);
goto fail;
}
CFArrayAppendValue(pia, pi);
CFReleaseSafe(pi);
}
if (!pia)
*der_p = NULL;
return pia;
fail:
CFReleaseNull(pia);
*der_p = NULL;
return NULL;
}
size_t SOSPeerInfoArrayGetDEREncodedSize(CFArrayRef pia, CFErrorRef *error) {
__block size_t array_size = 0;
__block bool fail = false;
CFArrayForEach(pia, ^(const void *value) {
if (isSOSPeerInfo(value)) {
SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
size_t pi_size = SOSPeerInfoGetDEREncodedSize(pi, error);
fail = (pi_size == 0);
array_size += pi_size;
} else {
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), (error != NULL ? *error : NULL), error);
fail = true;
}
});
return fail ? 0 : ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, array_size);
}
uint8_t* SOSPeerInfoArrayEncodeToDER(CFArrayRef pia, CFErrorRef* error, const uint8_t* der, uint8_t* der_end_param) {
uint8_t* const sequence_end = der_end_param;
__block uint8_t* der_end = der_end_param;
CFArrayForEachReverse(pia, ^(const void *value) {
SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
if (CFGetTypeID(pi) != SOSPeerInfoGetTypeID()) {
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), NULL, error);
der_end = NULL; }
if (der_end)
der_end = SOSPeerInfoEncodeToDER(pi, error, der, der_end);
});
if (der_end == NULL)
return NULL;
return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, sequence_end, der, der_end);
}
CFMutableSetRef SOSPeerInfoSetCreateFromArrayDER(CFAllocatorRef allocator, const CFSetCallBacks *callbacks, CFErrorRef* error,
const uint8_t** der_p, const uint8_t *der_end) {
CFMutableSetRef result = NULL;
CFArrayRef peers = SOSPeerInfoArrayCreateFromDER(allocator, error, der_p, der_end);
if (peers) {
result = CFSetCreateMutable(allocator, 0, callbacks);
CFSetSetValues(result, peers);
}
CFReleaseNull(peers);
return result;
}
size_t SOSPeerInfoSetGetDEREncodedArraySize(CFSetRef pia, CFErrorRef *error) {
__block size_t array_size = 0;
__block bool fail = false;
CFSetForEach(pia, ^(const void *value) {
if (isSOSPeerInfo(value)) {
SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
size_t pi_size = SOSPeerInfoGetDEREncodedSize(pi, error);
fail = (pi_size == 0);
array_size += pi_size;
} else {
SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Non SOSPeerInfo in array"), (error != NULL ? *error : NULL), error);
fail = true;
}
});
return fail ? 0 : ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, array_size);
}
uint8_t* SOSPeerInfoSetEncodeToArrayDER(CFSetRef pis, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
CFMutableArrayRef pia = CFSetCopyValues(pis);
CFArrayOfSOSPeerInfosSortByID(pia);
uint8_t* result = SOSPeerInfoArrayEncodeToDER(pia, error, der, der_end);
CFReleaseNull(pia);
return result;
}
CFArrayRef CreateArrayOfPeerInfoWithXPCObject(xpc_object_t peerArray, CFErrorRef* error) {
if (!peerArray) {
SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedNull, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Unexpected Null Array to encode"));
return NULL;
}
if (xpc_get_type(peerArray) != XPC_TYPE_DATA) {
SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("Array of peer info not data"));
return NULL;
}
const uint8_t* der = xpc_data_get_bytes_ptr(peerArray);
const uint8_t* der_end = der + xpc_data_get_length(peerArray);
return SOSPeerInfoArrayCreateFromDER(kCFAllocatorDefault, error, &der, der_end);
}
xpc_object_t CreateXPCObjectWithArrayOfPeerInfo(CFArrayRef array, CFErrorRef *error) {
size_t data_size = SOSPeerInfoArrayGetDEREncodedSize(array, error);
if (data_size == 0)
return NULL;
uint8_t *data = (uint8_t *)malloc(data_size);
if (!data) return NULL;
xpc_object_t result = NULL;
if (SOSPeerInfoArrayEncodeToDER(array, error, data, data + data_size))
result = xpc_data_create(data, data_size);
free(data);
return result;
}