#import <Foundation/Foundation.h>
#include <CommonCrypto/CommonDigest.h>
// Starts initialized to all 0xCC, total size of 88 bytes
typedef struct {
    uint64_t random; // At offset 0 bytes
    unsigned char token[32]; // At offset 8 bytes
    unsigned char data1[6]; // At offset 40
    unsigned char snStringBoardHash[16]; // At offset 46
    unsigned char zeroes[16]; // At offset 62. Edit: THIS IS WRONG, see Post #91 by WFH for corrected version
    unsigned char padding[10]; // At offset 78
 
} __attribute__((packed)) auth_info;
uint64_t generateRandomValue(void) {
    // Generate two 32-bit random numbers
    uint32_t high = arc4random();
    arc4random_stir();  // Stir the random number generator
    uint32_t low = arc4random();
 
    // Combine into 64-bit value
    uint64_t value = ((uint64_t)high << 32) | low;
    // Byte swap the 64-bit value
    return __builtin_bswap64(value);
}
NSData *getData1() {
    // Setup initial bytes with XOR operation
    // Initialize a buffer with specific values
    unsigned char buffer[4] = {0x2d, 0x30, 0x32, 0};
    // XOR each byte with 0x7F
    for (int i = 0; i < 3; i++) {
        buffer[i] ^= 0x7F;
    }
 
    // Create the registry path key
    NSString *registryKey = [NSString stringWithFormat:@"%@:%s",
        @"4D1EDE05-38C7-4a6a-9CC6-4BCCA8B38C14",
        buffer];
 
    // Get registry entry
    io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault,
        "IODeviceTree:/options");
    NSData *result = nil;
 
    if (entry != 0) {
        // Fetch property from registry
        CFTypeRef property = IORegistryEntryCreateCFProperty(entry,
            (__bridge CFStringRef) registryKey,
            kCFAllocatorDefault,
            0);
   
        // Release the registry entry
        IOObjectRelease(entry);
   
        if (property != NULL) {
            // Check if the property is of type CFData
            if (CFGetTypeID(property) == CFDataGetTypeID()) {
                result = (__bridge_transfer NSData *) property;
            } else {
                CFRelease(property);
            }
        }
    }
 
    return result;
}
NSString *getBoardID() {
    // Get the root IOService entry
    io_registry_entry_t registryEntry = IORegistryEntryFromPath(kIOMasterPortDefault,
        "IOService:/");
    NSString *boardID = nil;
 
    if (registryEntry != 0) {
        // Get the board-id property
        CFTypeRef property = IORegistryEntryCreateCFProperty(registryEntry,
            CFSTR("board-id"),
            kCFAllocatorDefault,
            0);
   
        // Release the registry entry as we no longer need it
        IOObjectRelease(registryEntry);
   
        if (CFGetTypeID(property) == CFDataGetTypeID()) {
            const char *bytes = (const char *)CFDataGetBytePtr((CFDataRef)property);
            boardID = [NSString stringWithCString:bytes
                encoding:NSUTF8StringEncoding];
        } else {
            CFRelease(property);
        }
    }
 
    return boardID;
}
NSString *getData2() {
    // Setup initial bytes and XOR them
    char bytes[4] = {0x32, 0x33, 0x3d, 0};  // "23="
 
    // XOR each byte with 0x7F
    for (int i = 0; i < 3; i++) {
        bytes[i] ^= 0x7F;
    }
 
    // Create the registry key
    NSString *registryKey = [NSString stringWithFormat:@"%@:%s",
        @"4D1EDE05-38C7-4a6a-9CC6-4BCCA8B38C14",
        bytes];
 
    // Get registry entry
    io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault,
        "IODeviceTree:/options");
    NSString *result = nil;
 
    if (entry != 0) {
        // Get property from registry
        CFTypeRef property = IORegistryEntryCreateCFProperty(entry,
            (__bridge CFStringRef)registryKey,
            kCFAllocatorDefault,
            0);
   
        // Release registry entry
        IOObjectRelease(entry);
   
        if (property != NULL) {
            if (CFGetTypeID(property) == CFDataGetTypeID()) {
                // Convert CFData to NSData
                NSData *propertyData = (__bridge_transfer NSData *)property;
           
                // Try to deserialize as property list
                NSError *error = nil;
                id propertyList = [NSPropertyListSerialization
                    propertyListWithData:propertyData
                    options:0
                    format:NULL
                    error:&error];
           
                // Check if result is a string
                if ([propertyList isKindOfClass:[NSString class]]) {
                    result = propertyList;
                }
            } else {
                CFRelease(property);
            }
        }
    }
 
    return result;
}
int main(int argc, char *argv[]) {
    @autoreleasepool {
        auth_info authInfo;
        assert(sizeof(auth_info) == 0x58);
        memset(&authInfo, 0xCC, sizeof(auth_info));
   
        authInfo.random = generateRandomValue();
   
        // Expect 64 hex characters (converts to 32 bytes)
        unsigned char authToken[32];
        // Take from session key of set-cookie header for initial request
        const char *hexCString = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        for (int i = 0; i < 32; i++) {
            unsigned int value;
            sscanf(hexCString + i * 2, "%2x", &value);
            authToken[i] = (unsigned char)value;
        }
        memcpy(authInfo.token, authToken, 32);
   
        // Copy first 6 bytes of data1 into authInfo.data1
        NSData *data1 = getData1();
        NSLog(@"Got data1 %@", data1);
        memcpy(authInfo.data1, [data1 bytes], MIN([data1 length], 6));
        // Get C strings for data2 and boardID
        const char *snString = [getData2() cStringUsingEncoding:NSASCIIStringEncoding];
        printf("Got snString %s\n", snString);
        const char *boardId = [getBoardID() cStringUsingEncoding:NSASCIIStringEncoding];
        printf("Got boardId %s\n", boardId);
   
        // Compute SHA256 hash of snString and boardId
        CC_SHA256_CTX shaCtx;
        CC_SHA256_Init(&shaCtx);
        CC_SHA256_Update(&shaCtx, snString, (CC_LONG)strlen(snString));
        CC_SHA256_Update(&shaCtx, boardId, (CC_LONG)strlen(boardId));
        unsigned char hash[CC_SHA256_DIGEST_LENGTH];
        CC_SHA256_Final(hash, &shaCtx);
        memcpy(authInfo.snStringBoardHash, hash, 16);
        memset(authInfo.zeroes, 0x0, 16);
   
        NSString *cidString = [NSString stringWithFormat:@"%016llX", CFSwapInt64(authInfo.random)];
   
        // Compute SHA256 hash of authInfo
        unsigned char authinfo_sha256[CC_SHA256_DIGEST_LENGTH];
        CC_SHA256(&authInfo, sizeof(auth_info), authinfo_sha256);
   
        // Convert the hash to a hex string. This becomes our K value
        NSMutableString *authinfoHex = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
        for (int idx = 0; idx < CC_SHA256_DIGEST_LENGTH; idx++) {
            [authinfoHex appendFormat:@"%02X", authinfo_sha256[idx]];
        }
   
        NSLog(@"%@", [NSString stringWithFormat:@"&cf_cid=%@sn=%sbid=%sk=%s",
            cidString,
            snString,
            boardId,
            [authinfoHex cStringUsingEncoding:NSASCIIStringEncoding]]);
    }
}