SOSGenCount.c   [plain text]


//
//  SOSGenCount.c
//  sec
//
//  Created by Richard Murphy on 1/29/15.
//
//

#include "SOSGenCount.h"
#include <utilities/SecCFWrappers.h>
#include <CoreFoundation/CFLocale.h>
#include <utilities/der_plist.h>
#include <utilities/der_plist_internal.h>


static CFAbsoluteTime SOSGenerationCountGetDateBits(int64_t genValue) {
    return (uint32_t) ((((uint64_t) genValue) >> 32) << 1);
}

void SOSGenerationCountWithDescription(SOSGenCountRef gen, void (^operation)(CFStringRef description)) {
    CFStringRef description = SOSGenerationCountCopyDescription(gen);

    operation(description);

    CFReleaseSafe(description);
}

CFStringRef SOSGenerationCountCopyDescription(SOSGenCountRef gen) {
    int64_t value = SOSGetGenerationSint(gen);

    CFMutableStringRef gcDecsription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));

    withStringOfAbsoluteTime(SOSGenerationCountGetDateBits(value), ^(CFStringRef decription) {
        CFStringAppend(gcDecsription, decription);
    });

    CFStringAppendFormat(gcDecsription, NULL, CFSTR(" %u]"), (uint32_t)(value & 0xFFFFFFFF));

    return gcDecsription;
}

int64_t SOSGetGenerationSint(SOSGenCountRef gen) {
    int64_t value;
    if(!gen) return 0;
    CFNumberGetValue(gen, kCFNumberSInt64Type, &value);
    return value;
}

static int64_t sosGenerationSetHighBits(int64_t value, int32_t high_31)
{
    value &= 0xFFFFFFFF; // Keep the low 32 bits.
    value |= ((int64_t) high_31) << 32;

    return value;
}

static SOSGenCountRef sosGenerationCreateOrIncrement(SOSGenCountRef gen) {
    int64_t value = 0;

    if(gen) {
        value = SOSGetGenerationSint(gen);
    }

    if((value >> 32) == 0) {
        uint32_t seconds = CFAbsoluteTimeGetCurrent(); // seconds
        value = sosGenerationSetHighBits(value, (seconds >> 1));
    }

    value++;
    return CFNumberCreate(NULL, kCFNumberSInt64Type, &value);
}

SOSGenCountRef SOSGenerationCreate() {
    return sosGenerationCreateOrIncrement(NULL);
}


// We need this for a circle gencount test
SOSGenCountRef SOSGenerationCreateWithValue(int64_t value) {
    return CFNumberCreate(NULL, kCFNumberSInt64Type, &value);
}

SOSGenCountRef SOSGenerationIncrementAndCreate(SOSGenCountRef gen) {
    return sosGenerationCreateOrIncrement(gen);
}

SOSGenCountRef SOSGenerationCopy(SOSGenCountRef gen) {
    if(!gen) return NULL;
    int64_t value = SOSGetGenerationSint(gen);
    return CFNumberCreate(NULL, kCFNumberSInt64Type, &value);
}

// Is current older than proposed?
bool SOSGenerationIsOlder(SOSGenCountRef older, SOSGenCountRef newer) {
    switch(CFNumberCompare(older, newer, NULL)) {
        case kCFCompareLessThan:  return true;
        case kCFCompareEqualTo: return false;
        case kCFCompareGreaterThan:  return false;
    }
    return false;
}

// Is current older than proposed?
static bool SOSGenerationIsOlderOrEqual(SOSGenCountRef older, SOSGenCountRef newer) {
    switch(CFNumberCompare(older, newer, NULL)) {
        case kCFCompareLessThan:  return true;
        case kCFCompareEqualTo: return true;
        case kCFCompareGreaterThan:  return false;
    }
    return false;
}


SOSGenCountRef SOSGenerationCreateWithBaseline(SOSGenCountRef reference) {
    SOSGenCountRef retval = SOSGenerationCreate();
    if(SOSGenerationIsOlderOrEqual(retval, reference)) {
        CFReleaseNull(retval);
        retval = SOSGenerationIncrementAndCreate(reference);
    }
    return retval;
}


SOSGenCountRef SOSGenCountCreateFromDER(CFAllocatorRef allocator, CFErrorRef* error,
                                        const uint8_t** der_p, const uint8_t *der_end) {
    SOSGenCountRef retval = NULL;
    *der_p = der_decode_number(allocator, &retval, error, *der_p, der_end);
    if(retval == NULL)
        retval = SOSGenerationCreate();
    return retval;
}

size_t SOSGenCountGetDEREncodedSize(SOSGenCountRef gencount, CFErrorRef *error) {
    return der_sizeof_number(gencount, error);
}

uint8_t *SOSGenCountEncodeToDER(SOSGenCountRef gencount, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
    return der_encode_number(gencount, error, der, der_end);
}