#include <SecureObjectSync/SOSTransport.h>
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
#include <dispatch/dispatch.h>
#include <stdlib.h>
static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.transport.error");
#define VECTOR_GROW(vector, count, capacity) \
do { \
if ((count) > capacity) { \
capacity = ((capacity) + 16) * 3 / 2; \
if (capacity < (count)) \
capacity = (count); \
vector = realloc((vector), sizeof(*(vector)) * capacity); \
} \
} while (0)
static void SOSDigestVectorEnsureCapacity(struct SOSDigestVector *dv, size_t count) {
VECTOR_GROW(dv->digest, count, dv->capacity);
}
void SOSDigestVectorReplaceAtIndex(struct SOSDigestVector *dv, size_t ix, const uint8_t *digest)
{
SOSDigestVectorEnsureCapacity(dv, ix + 1);
memcpy(dv->digest[ix], digest, SOSDigestSize);
dv->is_sorted = false;
}
static void SOSDigestVectorAppendOrdered(struct SOSDigestVector *dv, const uint8_t *digest)
{
SOSDigestVectorEnsureCapacity(dv, dv->count + 1);
memcpy(dv->digest[dv->count++], digest, SOSDigestSize);
}
void SOSDigestVectorAppend(struct SOSDigestVector *dv, const uint8_t *digest)
{
SOSDigestVectorAppendOrdered(dv, digest);
dv->is_sorted = false;
}
static int SOSDigestCompare(const void *a, const void *b)
{
return memcmp(a, b, SOSDigestSize);
}
void SOSDigestVectorSort(struct SOSDigestVector *dv)
{
qsort(dv->digest, dv->count, sizeof(*dv->digest), SOSDigestCompare);
dv->is_sorted = true;
}
bool SOSDigestVectorContains(struct SOSDigestVector *dv, const uint8_t *digest)
{
return SOSDigestVectorIndexOf(dv, digest) != (size_t)-1;
}
size_t SOSDigestVectorIndexOf(struct SOSDigestVector *dv, const uint8_t *digest)
{
if (!dv->is_sorted)
SOSDigestVectorSort(dv);
const void *pos = bsearch(digest, dv->digest, dv->count, sizeof(*dv->digest), SOSDigestCompare);
return pos ? ((size_t)(pos - (void *)dv->digest)) / SOSDigestSize : ((size_t)-1);
}
void SOSDigestVectorFree(struct SOSDigestVector *dv)
{
free(dv->digest);
dv->digest = NULL;
dv->count = 0;
dv->capacity = 0;
dv->is_sorted = false;
}
void SOSDigestVectorApply(struct SOSDigestVector *dv,
void *context, SOSDigestVectorApplyFunc func)
{
if (!dv->is_sorted)
SOSDigestVectorSort(dv);
for (size_t ix = 0; ix < dv->count; ++ix) {
func(context, dv->digest[ix]);
}
}
static void SOSDigestVectorAppendMultiple(struct SOSDigestVector *dv,
size_t count, const uint8_t *digests) {
if (count) {
SOSDigestVectorEnsureCapacity(dv, dv->count + count);
memcpy(dv->digest[dv->count], digests, count * SOSDigestSize);
dv->count += count;
}
}
void SOSDigestVectorDiff(struct SOSDigestVector *dv1, struct SOSDigestVector *dv2,
struct SOSDigestVector *dv1_2, struct SOSDigestVector *dv2_1)
{
assert(dv1_2->count == 0);
assert(dv2_1->count == 0);
if (!dv1->is_sorted)
SOSDigestVectorSort(dv1);
if (!dv2->is_sorted)
SOSDigestVectorSort(dv2);
size_t i1 = 0, i2 = 0;
while (i1 < dv1->count && i2 < dv2->count) {
int delta = SOSDigestCompare(dv1->digest[i1], dv2->digest[i2]);
if (delta == 0) {
++i1, ++i2;
} else if (delta < 0) {
SOSDigestVectorAppendOrdered(dv1_2, dv1->digest[i1++]);
} else {
SOSDigestVectorAppendOrdered(dv2_1, dv2->digest[i2++]);
}
}
SOSDigestVectorAppendMultiple(dv1_2, dv1->count - i1, dv1->digest[i1]);
SOSDigestVectorAppendMultiple(dv2_1, dv2->count - i2, dv2->digest[i2]);
dv1_2->is_sorted = true;
dv2_1->is_sorted = true;
}
bool SOSDigestVectorPatch(struct SOSDigestVector *base, struct SOSDigestVector *removals,
struct SOSDigestVector *additions, struct SOSDigestVector *dv,
CFErrorRef *error)
{
size_t base_ix = 0, removals_ix = 0, additions_ix = 0;
if (!base->is_sorted)
SOSDigestVectorSort(base);
if (!removals->is_sorted)
SOSDigestVectorSort(removals);
if (!additions->is_sorted)
SOSDigestVectorSort(additions);
assert(dv->count == 0);
SOSDigestVectorEnsureCapacity(dv, base->count - removals->count + additions->count);
dv->is_sorted = true;
while (base_ix < base->count) {
const uint8_t *d = base->digest[base_ix];
if (additions_ix < additions->count && SOSDigestCompare(d, additions->digest[additions_ix]) > 0) {
SOSDigestVectorAppendOrdered(dv, additions->digest[additions_ix++]);
} else if (removals_ix < removals->count && SOSDigestCompare(d, removals->digest[removals_ix]) == 0) {
base_ix++;
removals_ix++;
} else {
SOSDigestVectorAppendOrdered(dv, base->digest[base_ix++]);
}
}
if (removals_ix != removals->count) {
SecCFCreateErrorWithFormat(1, sErrorDomain, NULL, error, NULL, CFSTR("%lu extra removals left"), removals->count - removals_ix);
goto errOut;
}
while (additions_ix < additions->count) {
if (dv->count > 0 && SOSDigestCompare(dv->digest[dv->count - 1], additions->digest[additions_ix]) >= 0) {
SecCFCreateErrorWithFormat(1, sErrorDomain, NULL, error, NULL, CFSTR("unordered addition (%lu left)"), additions->count - additions_ix);
goto errOut;
}
SOSDigestVectorAppendOrdered(dv, additions->digest[additions_ix++]);
}
return true;
errOut:
return false;
}
struct __OpaqueSOSManifest {
};
SOSManifestRef SOSManifestCreateWithBytes(const uint8_t *bytes, size_t len,
CFErrorRef *error) {
SOSManifestRef manifest = (SOSManifestRef)CFDataCreate(NULL, bytes, (CFIndex)len);
if (!manifest)
SecCFCreateErrorWithFormat(kSOSManifestCreateError, sErrorDomain, NULL, error, NULL, CFSTR("Failed to create manifest"));
return manifest;
}
SOSManifestRef SOSManifestCreateWithData(CFDataRef data, CFErrorRef *error)
{
SOSManifestRef manifest = NULL;
if (data)
manifest = (SOSManifestRef)CFDataCreateCopy(kCFAllocatorDefault, data);
else
manifest = (SOSManifestRef)CFDataCreate(kCFAllocatorDefault, NULL, 0);
if (!manifest)
SecCFCreateErrorWithFormat(kSOSManifestCreateError, sErrorDomain, NULL, error, NULL, CFSTR("Failed to create manifest"));
return manifest;
}
void SOSManifestDispose(SOSManifestRef m) {
CFRelease(m);
}
size_t SOSManifestGetSize(SOSManifestRef m) {
return (size_t)CFDataGetLength((CFDataRef)m);
}
size_t SOSManifestGetCount(SOSManifestRef m) {
return SOSManifestGetSize(m) / SOSDigestSize;
}
const uint8_t *SOSManifestGetBytePtr(SOSManifestRef m) {
return CFDataGetBytePtr((CFDataRef)m);
}
CFDataRef SOSManifestGetData(SOSManifestRef m) {
return (CFDataRef)m;
}
bool SOSManifestDiff(SOSManifestRef a, SOSManifestRef b,
SOSManifestRef *a_minus_b, SOSManifestRef *b_minus_a,
CFErrorRef *error) {
bool result = true;
struct SOSDigestVector dva = SOSDigestVectorInit,
dvb = SOSDigestVectorInit,
dvab = SOSDigestVectorInit,
dvba = SOSDigestVectorInit;
SOSDigestVectorAppendMultiple(&dva, SOSManifestGetCount(a), SOSManifestGetBytePtr(a));
SOSDigestVectorAppendMultiple(&dvb, SOSManifestGetCount(b), SOSManifestGetBytePtr(b));
SOSDigestVectorDiff(&dva, &dvb, &dvab, &dvba);
SOSDigestVectorFree(&dva);
SOSDigestVectorFree(&dvb);
if (a_minus_b) {
*a_minus_b = SOSManifestCreateWithBytes((const uint8_t *)dvab.digest, dvab.count * SOSDigestSize, error);
if (!*a_minus_b)
result = false;
}
if (b_minus_a) {
*b_minus_a = SOSManifestCreateWithBytes((const uint8_t *)dvba.digest, dvba.count * SOSDigestSize, error);
if (!*b_minus_a)
result = false;
}
SOSDigestVectorFree(&dvab);
SOSDigestVectorFree(&dvba);
return result;
}
SOSManifestRef SOSManifestCreateWithPatch(SOSManifestRef base,
SOSManifestRef removals,
SOSManifestRef additions,
CFErrorRef *error) {
struct SOSDigestVector dvbase = SOSDigestVectorInit,
dvresult = SOSDigestVectorInit,
dvremovals = SOSDigestVectorInit,
dvadditions = SOSDigestVectorInit;
dvbase.is_sorted = dvresult.is_sorted = dvremovals.is_sorted = dvadditions.is_sorted = true;
SOSDigestVectorAppendMultiple(&dvbase, SOSManifestGetCount(base), SOSManifestGetBytePtr(base));
SOSDigestVectorAppendMultiple(&dvremovals, SOSManifestGetCount(removals), SOSManifestGetBytePtr(removals));
SOSDigestVectorAppendMultiple(&dvadditions, SOSManifestGetCount(additions), SOSManifestGetBytePtr(additions));
SOSManifestRef result;
if (SOSDigestVectorPatch(&dvbase, &dvremovals, &dvadditions, &dvresult, error)) {
result = SOSManifestCreateWithBytes((const uint8_t *)dvresult.digest, dvresult.count * SOSDigestSize, error);
} else {
result = NULL;
}
SOSDigestVectorFree(&dvbase);
SOSDigestVectorFree(&dvresult);
SOSDigestVectorFree(&dvremovals);
SOSDigestVectorFree(&dvadditions);
return result;
}
void SOSManifestForEach(SOSManifestRef m, void(^block)(CFDataRef e)) {
CFDataRef e;
const uint8_t *p, *q;
for (p = SOSManifestGetBytePtr(m), q = p + SOSManifestGetSize(m);
p + SOSDigestSize <= q; p += SOSDigestSize) {
e = CFDataCreateWithBytesNoCopy(0, p, SOSDigestSize, kCFAllocatorNull);
if (e) {
block(e);
CFRelease(e);
}
}
}
CFStringRef SOSManifestCopyDescription(SOSManifestRef m) {
CFMutableStringRef desc = CFStringCreateMutable(0, 0);
CFStringAppend(desc, CFSTR("<Manifest"));
SOSManifestForEach(m, ^(CFDataRef e) {
CFStringAppend(desc, CFSTR(" "));
const uint8_t *d = CFDataGetBytePtr(e);
CFStringAppendFormat(desc, 0, CFSTR("%02X%02X%02X%02X"), d[0], d[1], d[2], d[3]);
});
CFStringAppend(desc, CFSTR(">"));
return desc;
}
#if 0
SOSObjectRef SOSManifestGetObject(SOSManifestRef m, SOSObjectID k) {
return NULL;
}
void SOSManifestPutObject(SOSManifestRef m, SOSObjectID k, SOSObjectRef v) {
}
SOSManifestRef SOSManifestCreateSparse(void *get_ctx, SOSManifestGetF get_f) {
return NULL;
}
#endif