#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
#include <windows.h>
#define sleep(n) Sleep(n * 1000)
#endif
#include <curl/curl.h>
#include <plist/plist.h>
#include "common.h"
#include "libtatsu/tss.h"
#define USER_AGENT_STRING "InetURL/1.0"
#define AUTH_VERSION "1104.0.9"
#ifdef _WIN32
#define TSS_CLIENT_VERSION_STRING "libauthinstall_Win-"AUTH_VERSION""
#else
#define TSS_CLIENT_VERSION_STRING "libauthinstall-"AUTH_VERSION""
#endif
#define ECID_STRSIZE 0x20
typedef struct {
int length;
char* content;
} tss_response;
static int debug_level = 0;
static void error(const char* fmt, ...)
{
va_list args;
if (debug_level < 1)
return;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
static void debug(const char* fmt, ...)
{
va_list args;
if (debug_level < 2)
return;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
static void debug_plist(plist_t plist)
{
if (debug_level < 2)
return;
plist_write_to_stream(plist, stderr, PLIST_FORMAT_XML, PLIST_OPT_NONE);
}
#define GET_RAND(min, max) ((rand() % (max - min)) + min)
static char *generate_guid(void)
{
char *guid = (char *) malloc(sizeof(char) * 37);
const char *chars = "ABCDEF0123456789";
srand(time(NULL));
int i = 0;
for (i = 0; i < 36; i++) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
guid[i] = '-';
continue;
} else {
guid[i] = chars[GET_RAND(0, 16)];
}
}
guid[36] = '\0';
return guid;
}
plist_t tss_request_new(plist_t overrides)
{
plist_t request = plist_new_dict();
plist_dict_set_item(request, "@HostPlatformInfo",
#ifdef _WIN32
plist_new_string("windows")
#else
plist_new_string("mac")
#endif
);
plist_dict_set_item(request, "@VersionInfo", plist_new_string(TSS_CLIENT_VERSION_STRING));
char* guid = generate_guid();
if (guid) {
plist_dict_set_item(request, "@UUID", plist_new_string(guid));
free(guid);
}
if (overrides) {
plist_dict_merge(&request, overrides);
}
return request;
}
int tss_request_add_local_policy_tags(plist_t request, plist_t parameters)
{
plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1));
if (plist_dict_copy_bool(request, parameters, "Ap,LocalBoot", NULL) < 0) {
error("ERROR: Unable to find required Ap,LocalBoot in parameters\n");
return -1;
}
if (plist_dict_copy_item(request, parameters, "Ap,LocalPolicy", NULL) < 0) {
error("ERROR: Unable to find required Ap,LocalPolicy in parameters\n");
return -1;
}
if (plist_dict_copy_data(request, parameters, "Ap,NextStageIM4MHash", NULL) < 0) {
error("ERROR: Unable to find required Ap,NextStageIM4MHash in parameters\n");
return -1;
}
plist_dict_copy_data(request, parameters, "Ap,RecoveryOSPolicyNonceHash", NULL);
plist_dict_copy_data(request, parameters, "Ap,VolumeUUID", NULL);
plist_dict_copy_uint(request, parameters, "ApECID", NULL);
plist_dict_copy_uint(request, parameters, "ApChipID", NULL);
plist_dict_copy_uint(request, parameters, "ApBoardID", NULL);
plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL);
plist_dict_copy_data(request, parameters, "ApNonce", NULL);
if (!plist_dict_get_item(request, "ApSecurityMode")) {
if (plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL) < 0) {
error("ERROR: Unable to find required ApSecurityMode in parameters\n");
return -1;
}
}
if (!plist_dict_get_item(request, "ApProductionMode")) {
if (plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) {
error("ERROR: Unable to find required ApProductionMode in parameters\n");
return -1;
}
}
return 0;
}
int tss_parameters_add_from_manifest(plist_t parameters, plist_t build_identity, bool include_manifest)
{
plist_t node = NULL;
if (plist_dict_copy_data(parameters, build_identity, "UniqueBuildID", NULL) < 0) {
error("ERROR: Unable to find UniqueBuildID node\n");
return -1;
}
plist_dict_copy_string(parameters, build_identity, "Ap,OSLongVersion", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,OSReleaseType", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,ProductMarketingVersion", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,ProductType", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,SDKPlatform", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,Target", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,TargetType", NULL);
plist_dict_copy_string(parameters, build_identity, "Ap,Timestamp", NULL);
if (plist_dict_copy_uint(parameters, build_identity, "ApChipID", NULL) < 0) {;
error("ERROR: Unable to find ApChipID node\n");
return -1;
}
if (plist_dict_copy_uint(parameters, build_identity, "ApBoardID", NULL) < 0) {
error("ERROR: Unable to find ApBoardID node\n");
return -1;
}
plist_dict_copy_uint(parameters, build_identity, "ApSecurityDomain", NULL);
plist_dict_copy_uint(parameters, build_identity, "BMU,BoardID", NULL);
plist_dict_copy_uint(parameters, build_identity, "BMU,ChipID", NULL);
if (plist_dict_copy_uint(parameters, build_identity, "BbChipID", NULL) < 0) {
debug("NOTE: Unable to find BbChipID node\n");
}
if (plist_dict_copy_data(parameters, build_identity, "BbProvisioningManifestKeyHash", NULL) < 0) {
debug("NOTE: Unable to find BbProvisioningManifestKeyHash node\n");
}
if (plist_dict_copy_data(parameters, build_identity, "BbActivationManifestKeyHash", NULL) < 0) {
debug("NOTE: Unable to find BbActivationManifestKeyHash node\n");
}
if (plist_dict_copy_data(parameters, build_identity, "BbCalibrationManifestKeyHash", NULL) < 0) {
debug("NOTE: Unable to find BbCalibrationManifestKeyHash node\n");
}
if (plist_dict_copy_data(parameters, build_identity, "BbFactoryActivationManifestKeyHash", NULL) < 0) {
debug("NOTE: Unable to find BbFactoryActivationManifestKeyHash node\n");
}
if (plist_dict_copy_data(parameters, build_identity, "BbFDRSecurityKeyHash", NULL) < 0) {
debug("NOTE: Unable to find BbFDRSecurityKeyHash node\n");
}
if (plist_dict_copy_data(parameters, build_identity, "BbSkeyId", NULL) < 0) {
debug("NOTE: Unable to find BbSkeyId node\n");
}
plist_dict_copy_uint(parameters, build_identity, "SE,ChipID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Savage,ChipID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Savage,PatchEpoch", NULL);
plist_dict_copy_uint(parameters, build_identity, "Yonkers,BoardID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Yonkers,ChipID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Yonkers,PatchEpoch", NULL);
plist_dict_copy_uint(parameters, build_identity, "Rap,BoardID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Rap,ChipID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Rap,SecurityDomain", NULL);
plist_dict_copy_uint(parameters, build_identity, "Baobab,BoardID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Baobab,ChipID", NULL);
plist_dict_copy_uint(parameters, build_identity, "Baobab,ManifestEpoch", NULL);
plist_dict_copy_uint(parameters, build_identity, "Baobab,SecurityDomain", NULL);
plist_dict_copy_uint(parameters, build_identity, "eUICC,ChipID", NULL);
plist_dict_copy_uint(parameters, build_identity, "NeRDEpoch", NULL);
plist_dict_copy_data(parameters, build_identity, "PearlCertificationRootPub", NULL);
plist_dict_copy_bool(parameters, build_identity, "AllowNeRDBoot", NULL);
if (plist_dict_get_item(parameters, "NeRDEpoch")) {
plist_dict_set_item(parameters, "PermitNeRDPivot", plist_new_data(NULL, 0));
}
plist_dict_copy_uint(parameters, build_identity, "Timer,BoardID,1", NULL);
plist_dict_copy_uint(parameters, build_identity, "Timer,BoardID,2", NULL);
plist_dict_copy_uint(parameters, build_identity, "Timer,ChipID,1", NULL);
plist_dict_copy_uint(parameters, build_identity, "Timer,ChipID,2", NULL);
plist_dict_copy_uint(parameters, build_identity, "Timer,SecurityDomain,1", NULL);
plist_dict_copy_uint(parameters, build_identity, "Timer,SecurityDomain,2", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,ChipID", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,Type", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,SubType", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,ProductClass", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,UseProductClass", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,NonceDomain", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,Version", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,PreauthorizationVersion", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,FakeRoot", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemOS", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemVolume", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,SystemTrustCache", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppOS", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppVolume", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,AppTrustCache", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainOS", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainVolume", NULL);
plist_dict_copy_item(parameters, build_identity, "Cryptex1,MobileAssetBrainTrustCache", NULL);
plist_dict_copy_item(parameters, build_identity, "USBPortController1,BoardID", NULL);
plist_dict_copy_item(parameters, build_identity, "USBPortController1,ChipID", NULL);
plist_dict_copy_item(parameters, build_identity, "USBPortController1,SecurityDomain", NULL);
node = plist_dict_get_item(build_identity, "Info");
if (node) {
plist_dict_copy_bool(parameters, node, "RequiresUIDMode", NULL);
}
if (include_manifest) {
node = plist_dict_get_item(build_identity, "Manifest");
if (!node || plist_get_node_type(node) != PLIST_DICT) {
error("ERROR: Unable to find Manifest node\n");
return -1;
}
plist_dict_set_item(parameters, "Manifest", plist_copy(node));
}
return 0;
}
int tss_request_add_ap_img4_tags(plist_t request, plist_t parameters)
{
if (!parameters) {
error("ERROR: Missing required AP parameters\n");
return -1;
}
plist_dict_copy_string(request, parameters, "Ap,OSLongVersion", NULL);
plist_dict_copy_string(request, parameters, "Ap,OSReleaseType", NULL);
plist_dict_copy_string(request, parameters, "Ap,ProductMarketingVersion", NULL);
plist_dict_copy_string(request, parameters, "Ap,ProductType", NULL);
plist_dict_copy_string(request, parameters, "Ap,SDKPlatform", NULL);
plist_dict_copy_string(request, parameters, "Ap,Target", NULL);
plist_dict_copy_string(request, parameters, "Ap,TargetType", NULL);
plist_dict_copy_string(request, parameters, "Ap,Timestamp", NULL);
if (plist_dict_copy_data(request, parameters, "ApNonce", NULL) < 0) {
error("ERROR: Unable to find required ApNonce in parameters\n");
return -1;
}
plist_dict_set_item(request, "@ApImg4Ticket", plist_new_bool(1));
if (!plist_dict_get_item(request, "ApSecurityMode")) {
if (plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL) < 0) {
error("ERROR: Unable to find required ApSecurityMode in parameters\n");
return -1;
}
}
if (!plist_dict_get_item(request, "ApProductionMode")) {
if (plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) {
error("ERROR: Unable to find required ApProductionMode in parameters\n");
return -1;
}
}
plist_dict_copy_data(request, parameters, "SepNonce", plist_dict_get_item(parameters, "SepNonce") ? NULL : "ApSepNonce");
plist_dict_copy_uint(request, parameters, "NeRDEpoch", NULL);
plist_dict_copy_data(request, parameters, "PearlCertificationRootPub", NULL);
plist_dict_copy_bool(request, parameters, "AllowNeRDBoot", NULL);
plist_dict_copy_item(request, parameters, "PermitNeRDPivot", NULL);
if (plist_dict_get_item(parameters, "UID_MODE")) {
plist_dict_copy_item(request, parameters, "UID_MODE", NULL);
} else if (plist_dict_get_bool(parameters, "RequiresUIDMode")) {
plist_dict_set_item(request, "UID_MODE", plist_new_bool(0));
}
if (plist_dict_get_item(parameters, "ApSikaFuse")) {
plist_dict_copy_item(request, parameters, "Ap,SikaFuse", "ApSikaFuse");
} else if (plist_dict_get_bool(parameters, "RequiresUIDMode")) {
plist_dict_set_item(request, "Ap,SikaFuse", plist_new_int(0));
}
return 0;
}
int tss_request_add_ap_img3_tags(plist_t request, plist_t parameters)
{
if (!parameters) {
error("ERROR: Missing required AP parameters\n");
return -1;
}
if (plist_dict_copy_data(request, parameters, "ApNonce", NULL) < 0) {
error("WARNING: Unable to find ApNonce in parameters\n");
}
plist_dict_set_item(request, "@APTicket", plist_new_bool(1));
if (plist_dict_copy_uint(request, parameters, "ApBoardID", NULL) < 0) {
error("ERROR: Unable to find required ApBoardID in request\n");
return -1;
}
if (plist_dict_copy_uint(request, parameters, "ApChipID", NULL) < 0) {
error("ERROR: Unable to find required ApChipID in request\n");
return -1;
}
if (plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL) < 0) {
error("ERROR: Unable to find required ApSecurityDomain in request\n");
return -1;
}
if (plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL) < 0) {
error("ERROR: Unable to find required ApProductionMode in parameters\n");
return -1;
}
return 0;
}
int tss_request_add_common_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_dict_copy_uint(request, parameters, "ApECID", NULL);
plist_dict_copy_data(request, parameters, "UniqueBuildID", NULL);
plist_dict_copy_uint(request, parameters, "ApChipID", NULL);
plist_dict_copy_uint(request, parameters, "ApBoardID", NULL);
plist_dict_copy_uint(request, parameters, "ApSecurityDomain", NULL);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
static void tss_entry_apply_restore_request_rules(plist_t tss_entry, plist_t parameters, plist_t rules)
{
if (!tss_entry || !rules) {
return;
}
if (plist_get_node_type(tss_entry) != PLIST_DICT) {
return;
}
if (plist_get_node_type(rules) != PLIST_ARRAY) {
return;
}
uint32_t i;
for (i = 0; i < plist_array_get_size(rules); i++) {
plist_t rule = plist_array_get_item(rules, i);
plist_t conditions = plist_dict_get_item(rule, "Conditions");
plist_dict_iter iter = NULL;
plist_dict_new_iter(conditions, &iter);
char* key = NULL;
plist_t value = NULL;
plist_t value2 = NULL;
int conditions_fulfilled = 1;
while (conditions_fulfilled) {
plist_dict_next_item(conditions, iter, &key, &value);
if (key == NULL)
break;
if (!strcmp(key, "ApRawProductionMode")) {
value2 = plist_dict_get_item(parameters, "ApProductionMode");
} else if (!strcmp(key, "ApCurrentProductionMode")) {
value2 = plist_dict_get_item(parameters, "ApProductionMode");
} else if (!strcmp(key, "ApRawSecurityMode")) {
value2 = plist_dict_get_item(parameters, "ApSecurityMode");
} else if (!strcmp(key, "ApRequiresImage4")) {
value2 = plist_dict_get_item(parameters, "ApSupportsImg4");
} else if (!strcmp(key, "ApDemotionPolicyOverride")) {
value2 = plist_dict_get_item(parameters, "DemotionPolicy");
} else if (!strcmp(key, "ApInRomDFU")) {
value2 = plist_dict_get_item(parameters, "ApInRomDFU");
} else {
error("WARNING: Unhandled condition '%s' while parsing RestoreRequestRules\n", key);
value2 = NULL;
}
if (value2) {
conditions_fulfilled = plist_compare_node_value(value, value2);
} else {
conditions_fulfilled = 0;
}
free(key);
}
free(iter);
iter = NULL;
if (!conditions_fulfilled) {
continue;
}
plist_t actions = plist_dict_get_item(rule, "Actions");
plist_dict_new_iter(actions, &iter);
while (1) {
plist_dict_next_item(actions, iter, &key, &value);
if (key == NULL)
break;
uint8_t bv = 255;
plist_get_bool_val(value, &bv);
if (bv != 255) {
value2 = plist_dict_get_item(tss_entry, key);
if (value2) {
plist_dict_remove_item(tss_entry, key);
}
debug("DEBUG: Adding %s=%s to TSS entry\n", key, (bv) ? "true" : "false");
plist_dict_set_item(tss_entry, key, plist_new_bool(bv));
}
free(key);
}
}
}
static int is_fw_payload(plist_t info_dict)
{
return plist_dict_get_bool(info_dict, "IsFirmwarePayload")
|| plist_dict_get_bool(info_dict, "IsSecondaryFirmwarePayload")
|| plist_dict_get_bool(info_dict, "IsFUDFirmware")
|| plist_dict_get_bool(info_dict, "IsLoadedByiBoot")
|| plist_dict_get_bool(info_dict, "IsEarlyAccessFirmware")
|| plist_dict_get_bool(info_dict, "IsiBootEANFirmware")
|| plist_dict_get_bool(info_dict, "IsiBootNonEssentialFirmware");
}
int tss_request_add_ap_recovery_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: Unable to find restore manifest\n");
return -1;
}
char* key = NULL;
plist_t manifest_entry = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (1) {
free(key);
key = NULL;
plist_dict_next_item(manifest_node, iter, &key, &manifest_entry);
if (key == NULL)
break;
if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) {
error("ERROR: Unable to fetch BuildManifest entry\n");
free(key);
return -1;
}
if ((strcmp(key, "BasebandFirmware") == 0)) {
continue;
}
if ((strcmp(key, "SE,UpdatePayload") == 0)) {
continue;
}
if ((strcmp(key, "BaseSystem") == 0)) {
continue;
}
if ((strcmp(key, "ANS") == 0)) {
continue;
}
if ((strcmp(key, "Ap,AudioBootChime") == 0)) {
continue;
}
if ((strcmp(key, "Ap,CIO") == 0)) {
continue;
}
if ((strcmp(key, "Ap,RestoreCIO") == 0)) {
continue;
}
if ((strcmp(key, "Ap,RestoreTMU") == 0)) {
continue;
}
if ((strcmp(key, "Ap,TMU") == 0)) {
continue;
}
if ((strcmp(key, "Ap,rOSLogo1") == 0)) {
continue;
}
if ((strcmp(key, "Ap,rOSLogo2") == 0)) {
continue;
}
if ((strcmp(key, "AppleLogo") == 0)) {
continue;
}
if ((strcmp(key, "DCP") == 0)) {
continue;
}
if ((strcmp(key, "LLB") == 0)) {
continue;
}
if ((strcmp(key, "RecoveryMode") == 0)) {
continue;
}
if ((strcmp(key, "RestoreANS") == 0)) {
continue;
}
if ((strcmp(key, "RestoreDCP") == 0)) {
continue;
}
if ((strcmp(key, "RestoreDeviceTree") == 0)) {
continue;
}
if ((strcmp(key, "RestoreKernelCache") == 0)) {
continue;
}
if ((strcmp(key, "RestoreLogo") == 0)) {
continue;
}
if ((strcmp(key, "RestoreRamDisk") == 0)) {
continue;
}
if ((strcmp(key, "RestoreSEP") == 0)) {
continue;
}
if ((strcmp(key, "SEP") == 0)) {
continue;
}
if ((strcmp(key, "ftap") == 0)) {
continue;
}
if ((strcmp(key, "ftsp") == 0)) {
continue;
}
if ((strcmp(key, "iBEC") == 0)) {
continue;
}
if ((strcmp(key, "iBSS") == 0)) {
continue;
}
if ((strcmp(key, "rfta") == 0)) {
continue;
}
if ((strcmp(key, "rfts") == 0)) {
continue;
}
if (strcmp(key, "Diags") == 0) {
continue;
}
plist_t info_dict = plist_dict_get_item(manifest_entry, "Info");
if (!info_dict) {
continue;
}
if (plist_dict_get_bool(parameters, "_OnlyFWComponents")) {
if (!plist_dict_get_bool(manifest_entry, "Trusted")) {
debug("DEBUG: %s: Skipping '%s' as it is not trusted\n", __func__, key);
continue;
}
if (!is_fw_payload(info_dict)) {
debug("DEBUG: %s: Skipping '%s' as it is not a firmware payload\n", __func__, key);
continue;
}
}
plist_t tss_entry = plist_copy(manifest_entry);
plist_dict_remove_item(tss_entry, "Info");
plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules");
if (rules) {
debug("DEBUG: Applying restore request rules for entry %s\n", key);
tss_entry_apply_restore_request_rules(tss_entry, parameters, rules);
}
if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) {
debug("DEBUG: No Digest data, using empty value for entry %s\n", key);
plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0));
}
plist_dict_set_item(request, key, tss_entry);
}
free(key);
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_ap_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: Unable to find restore manifest\n");
return -1;
}
char* key = NULL;
plist_t manifest_entry = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (1) {
free(key);
key = NULL;
plist_dict_next_item(manifest_node, iter, &key, &manifest_entry);
if (key == NULL)
break;
if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) {
error("ERROR: Unable to fetch BuildManifest entry\n");
free(key);
return -1;
}
if ((strcmp(key, "BasebandFirmware") == 0)) {
continue;
}
if ((strcmp(key, "SE,UpdatePayload") == 0)) {
continue;
}
if ((strcmp(key, "BaseSystem") == 0)) {
continue;
}
if (strcmp(key, "Diags") == 0) {
continue;
}
if (strcmp(key, "Ap,ExclaveOS") == 0) {
continue;
}
plist_t info_dict = plist_dict_get_item(manifest_entry, "Info");
if (!info_dict) {
continue;
}
uint8_t trusted = plist_dict_get_bool(manifest_entry, "Trusted");
uint8_t supports_img4 = plist_dict_get_bool(parameters, "ApSupportsImg4");
if (supports_img4) {
if (!plist_dict_get_item(info_dict, "RestoreRequestRules") && !trusted) {
debug("DEBUG: %s: Skipping '%s' as it doesn't have RestoreRequestRules and is not Trusted\n", __func__, key);
continue;
}
}
if (plist_dict_get_bool(parameters, "_OnlyFWComponents")) {
if (!trusted) {
debug("DEBUG: %s: Skipping '%s' as it is not trusted\n", __func__, key);
continue;
}
if (!is_fw_payload(info_dict)) {
debug("DEBUG: %s: Skipping '%s' as it is not a firmware payload\n", __func__, key);
continue;
}
}
if (plist_dict_get_bool(info_dict, "IsFTAB")) {
debug("DEBUG: %s: Skipping FTAB component '%s'\n", __func__, key);
continue;
}
plist_t tss_entry = plist_copy(manifest_entry);
plist_dict_remove_item(tss_entry, "Info");
plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules");
if (rules) {
debug("DEBUG: Applying restore request rules for entry %s\n", key);
tss_entry_apply_restore_request_rules(tss_entry, parameters, rules);
} else if (supports_img4) {
plist_dict_copy_bool(tss_entry, parameters, "EPRO", "ApProductionMode");
plist_dict_copy_bool(tss_entry, parameters, "ESEC", "ApSecurityMode");
}
if (trusted && !plist_dict_get_item(manifest_entry, "Digest")) {
debug("DEBUG: No Digest data, using empty value for entry %s\n", key);
plist_dict_set_item(tss_entry, "Digest", plist_new_data(NULL, 0));
}
if (plist_dict_get_size(tss_entry) == 0) {
continue;
}
plist_dict_set_item(request, key, tss_entry);
}
free(key);
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_baseband_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t node = NULL;
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_copy_uint(request, parameters, "BbChipID", NULL);
plist_dict_copy_data(request, parameters, "BbProvisioningManifestKeyHash", NULL);
plist_dict_copy_data(request, parameters, "BbActivationManifestKeyHash", NULL);
plist_dict_copy_data(request, parameters, "BbCalibrationManifestKeyHash", NULL);
plist_dict_copy_data(request, parameters, "BbFactoryActivationManifestKeyHash", NULL);
plist_dict_copy_data(request, parameters, "BbFDRSecurityKeyHash", NULL);
plist_dict_copy_data(request, parameters, "BbSkeyId", NULL);
plist_dict_copy_data(request, parameters, "BbNonce", NULL);
plist_dict_copy_uint(request, parameters, "BbGoldCertId", NULL);
uint64_t bb_chip_id = plist_dict_get_uint(request, "BbChipID");
uint32_t bb_cert_id = (uint32_t)plist_dict_get_uint(request, "BbGoldCertId");
if (plist_dict_copy_data(request, parameters, "BbSNUM", NULL) < 0) {
error("ERROR: Unable to find required BbSNUM in parameters\n");
return -1;
}
node = plist_access_path(parameters, 2, "Manifest", "BasebandFirmware");
if (!node || plist_get_node_type(node) != PLIST_DICT) {
error("ERROR: Unable to get BasebandFirmware node\n");
return -1;
}
plist_t bbfwdict = plist_copy(node);
node = NULL;
if (plist_dict_get_item(bbfwdict, "Info")) {
plist_dict_remove_item(bbfwdict, "Info");
}
if (bb_chip_id == 0x68) {
if (bb_cert_id == 0x26F3FACC || bb_cert_id == 0x5CF2EC4E || bb_cert_id == 0x8399785A) {
plist_dict_remove_item(bbfwdict, "PSI2-PartialDigest");
plist_dict_remove_item(bbfwdict, "RestorePSI2-PartialDigest");
} else {
plist_dict_remove_item(bbfwdict, "PSI-PartialDigest");
plist_dict_remove_item(bbfwdict, "RestorePSI-PartialDigest");
}
}
plist_dict_set_item(request, "BasebandFirmware", bbfwdict);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_se_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
if (plist_dict_copy_uint(request, parameters, "SE,ChipID", NULL) < 0) {
error("ERROR: %s: Unable to find required SE,ChipID in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_data(request, parameters, "SE,ID", NULL) < 0) {
error("ERROR: %s: Unable to find required SE,ID in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_data(request, parameters, "SE,Nonce", NULL) < 0) {
error("ERROR: %s: Unable to find required SE,Nonce in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_data(request, parameters, "SE,RootKeyIdentifier", NULL) < 0) {
error("ERROR: %s: Unable to find required SE,RootKeyIdentifier in parameters\n", __func__);
return -1;
}
uint8_t is_dev = plist_dict_get_bool(parameters, "SE,IsDev");
char* key = NULL;
plist_t manifest_entry = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (1) {
free(key);
key = NULL;
plist_dict_next_item(manifest_node, iter, &key, &manifest_entry);
if (key == NULL)
break;
if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) {
error("ERROR: Unable to fetch BuildManifest entry\n");
free(key);
return -1;
}
if (strncmp(key, "SE,", 3)) {
continue;
}
plist_t tss_entry = plist_copy(manifest_entry);
plist_dict_remove_item(tss_entry, "Info");
if (is_dev) {
if (plist_dict_get_item(tss_entry, "ProductionCMAC"))
plist_dict_remove_item(tss_entry, "ProductionCMAC");
if (plist_dict_get_item(tss_entry, "ProductionUpdatePayloadHash"))
plist_dict_remove_item(tss_entry, "ProductionUpdatePayloadHash");
} else {
if (plist_dict_get_item(tss_entry, "DevelopmentCMAC"))
plist_dict_remove_item(tss_entry, "DevelopmentCMAC");
if (plist_dict_get_item(tss_entry, "DevelopmentUpdatePayloadHash"))
plist_dict_remove_item(tss_entry, "DevelopmentUpdatePayloadHash");
}
plist_dict_set_item(request, key, tss_entry);
}
free(key);
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
if (!plist_dict_get_item(request, "@SE2,Ticket") && !plist_dict_get_item(request, "@SE,Ticket")) {
plist_dict_set_item(request, "@SE,Ticket", plist_new_bool(1));
}
return 0;
}
int tss_request_add_savage_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name)
{
plist_t node = NULL;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_set_item(request, "@Savage,Ticket", plist_new_bool(1));
if (plist_dict_copy_data(request, parameters, "Savage,UID", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,UID in parameters\n", __func__);
return -1;
}
node = plist_access_path(manifest_node, 2, "SEP", "Digest");
if (!node) {
error("ERROR: Unable to get SEP digest from manifest\n");
return -1;
}
plist_t dict = plist_new_dict();
plist_dict_set_item(dict, "Digest", plist_copy(node));
plist_dict_set_item(request, "SEP", dict);
if (plist_dict_copy_uint(request, parameters, "Savage,PatchEpoch", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,PatchEpoch in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_uint(request, parameters, "Savage,ChipID", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,ChipID in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_bool(request, parameters, "Savage,AllowOfflineBoot", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,AllowOfflineBoot in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_bool(request, parameters, "Savage,ReadFWKey", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,ReadFWKey in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_bool(request, parameters, "Savage,ProductionMode", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,ProductionMode in parameters\n", __func__);
return -1;
}
const char *comp_name = NULL;
uint8_t isprod = plist_dict_get_bool(request, "Savage,ProductionMode");
comp_name = (isprod) ? "Savage,B0-Prod-Patch" : "Savage,B0-Dev-Patch";
node = plist_dict_get_item(parameters, "Savage,Revision");
if (node && (plist_get_node_type(node) == PLIST_DATA)) {
uint64_t savage_rev_len = 0;
const unsigned char *savage_rev = (const unsigned char*)plist_get_data_ptr(node, &savage_rev_len);
if (savage_rev_len > 0) {
if (((savage_rev[0] | 0x10) & 0xF0) == 0x30) {
comp_name = (isprod) ? "Savage,B2-Prod-Patch" : "Savage,B2-Dev-Patch";
} else if ((savage_rev[0] & 0xF0) == 0xA0) {
comp_name = (isprod) ? "Savage,BA-Prod-Patch" : "Savage,BA-Dev-Patch";
}
}
}
node = plist_dict_get_item(manifest_node, comp_name);
if (!node) {
error("ERROR: Unable to get %s entry from manifest\n", comp_name);
return -1;
}
dict = plist_copy(node);
plist_dict_remove_item(dict, "Info");
plist_dict_set_item(request, comp_name, dict);
if (component_name) {
*component_name = strdup(comp_name);
}
if (plist_dict_copy_data(request, parameters, "Savage,Nonce", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,Nonce in parameters\n", __func__);
return -1;
}
if (plist_dict_copy_bool(request, parameters, "Savage,ReadECKey", NULL) < 0) {
error("ERROR: %s: Unable to find required Savage,ReadECKey in parameters\n", __func__);
return -1;
}
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_yonkers_tags(plist_t request, plist_t parameters, plist_t overrides, char **component_name)
{
plist_t node = NULL;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_set_item(request, "@Yonkers,Ticket", plist_new_bool(1));
node = plist_access_path(manifest_node, 2, "SEP", "Digest");
if (!node) {
error("ERROR: Unable to get SEP digest from manifest\n");
return -1;
}
plist_t dict = plist_new_dict();
plist_dict_set_item(dict, "Digest", plist_copy(node));
plist_dict_set_item(request, "SEP", dict);
{
static const char *keys[] = {"Yonkers,AllowOfflineBoot", "Yonkers,BoardID", "Yonkers,ChipID", "Yonkers,ECID", "Yonkers,Nonce", "Yonkers,PatchEpoch", "Yonkers,ProductionMode", "Yonkers,ReadECKey", "Yonkers,ReadFWKey", };
int i;
for (i = 0; i < (int)(sizeof(keys) / sizeof(keys[0])); ++i) {
node = plist_dict_get_item(parameters, keys[i]);
if (!node) {
error("ERROR: %s: Unable to find required %s in parameters\n", __func__, keys[i]);
}
plist_dict_set_item(request, keys[i], plist_copy(node));
node = NULL;
}
}
char *comp_name = NULL;
plist_t comp_node = NULL;
uint8_t isprod = plist_dict_get_bool(parameters, "Yonkers,ProductionMode");
uint64_t fabrevision = plist_dict_get_uint(parameters, "Yonkers,FabRevision");
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (iter) {
node = NULL;
comp_name = NULL;
plist_dict_next_item(manifest_node, iter, &comp_name, &node);
if (comp_name == NULL) {
node = NULL;
break;
}
if (strncmp(comp_name, "Yonkers,", 8) == 0) {
int target_node = 1;
plist_t sub_node;
if ((sub_node = plist_dict_get_item(node, "EPRO")) != NULL && plist_get_node_type(sub_node) == PLIST_BOOLEAN) {
uint8_t b = 0;
plist_get_bool_val(sub_node, &b);
target_node &= ((isprod) ? b : !b);
}
if ((sub_node = plist_dict_get_item(node, "FabRevision")) != NULL && plist_get_node_type(sub_node) == PLIST_UINT) {
uint64_t v = 0;
plist_get_uint_val(sub_node, &v);
target_node &= (v == fabrevision);
}
if (target_node) {
comp_node = node;
break;
}
}
free(comp_name);
}
free(iter);
if (comp_name == NULL) {
error("ERROR: No Yonkers node for %s/%lu\n", (isprod) ? "Production" : "Development", (unsigned long)fabrevision);
return -1;
}
if (comp_node != NULL) {
plist_t comp_dict = plist_copy(comp_node);
plist_dict_remove_item(comp_dict, "Info");
plist_dict_set_item(request, comp_name, comp_dict);
}
if (component_name) {
*component_name = comp_name;
} else {
free(comp_name);
}
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_vinyl_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t node = NULL;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_set_item(request, "@eUICC,Ticket", plist_new_bool(1));
plist_dict_copy_bool(request, parameters, "eUICC,ApProductionMode", "ApProductionMode");
plist_dict_copy_uint(request, parameters, "eUICC,ChipID", NULL);
plist_dict_copy_data(request, parameters, "eUICC,EID", NULL);
plist_dict_copy_data(request, parameters, "eUICC,RootKeyIdentifier", NULL);
if (!plist_dict_get_item(request, "eUICC,Gold")) {
plist_t n = plist_access_path(parameters, 2, "Manifest", "eUICC,Gold");
if (n) {
plist_t p = plist_new_dict();
plist_dict_copy_data(p, n, "Digest", NULL);
plist_dict_set_item(request, "eUICC,Gold", p);
}
}
if (!plist_dict_get_item(request, "eUICC,Main")) {
plist_t n = plist_access_path(parameters, 2, "Manifest", "eUICC,Main");
if (n) {
plist_t p = plist_new_dict();
plist_dict_copy_data(p, n, "Digest", NULL);
plist_dict_set_item(request, "eUICC,Main", p);
}
}
node = plist_dict_get_item(parameters, "EUICCGoldNonce");
if (node) {
plist_t n = plist_dict_get_item(request, "eUICC,Gold");
if (n) {
plist_dict_set_item(n, "Nonce", plist_copy(node));
}
}
node = plist_dict_get_item(parameters, "EUICCMainNonce");
if (node) {
plist_t n = plist_dict_get_item(request, "eUICC,Main");
if (n) {
plist_dict_set_item(n, "Nonce", plist_copy(node));
}
}
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_rose_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t node = NULL;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_set_item(request, "@Rap,Ticket", plist_new_bool(1));
plist_dict_copy_uint(request, parameters, "Rap,BoardID", NULL);
plist_dict_copy_uint(request, parameters, "Rap,ChipID", NULL);
plist_dict_copy_uint(request, parameters, "Rap,ECID", NULL);
plist_dict_copy_data(request, parameters, "Rap,Nonce", NULL);
plist_dict_copy_bool(request, parameters, "Rap,ProductionMode", NULL);
plist_dict_copy_uint(request, parameters, "Rap,SecurityDomain", NULL);
plist_dict_copy_bool(request, parameters, "Rap,SecurityMode", NULL);
plist_dict_copy_data(request, parameters, "Rap,FdrRootCaDigest", NULL);
char *comp_name = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (iter) {
node = NULL;
comp_name = NULL;
plist_dict_next_item(manifest_node, iter, &comp_name, &node);
if (comp_name == NULL) {
node = NULL;
break;
}
if (strncmp(comp_name, "Rap,", 4) == 0) {
plist_t manifest_entry = plist_copy(node);
plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules");
if (rules) {
debug("DEBUG: Applying restore request rules for entry %s\n", comp_name);
tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules);
}
if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) {
debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name);
plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0));
}
plist_dict_remove_item(manifest_entry, "Info");
plist_dict_set_item(request, comp_name, manifest_entry);
}
free(comp_name);
}
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_veridian_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t node = NULL;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_set_item(request, "@BMU,Ticket", plist_new_bool(1));
plist_dict_copy_uint(request, parameters, "BMU,BoardID", NULL);
plist_dict_copy_uint(request, parameters, "BMU,ChipID", "ChipID");
plist_dict_copy_data(request, parameters, "BMU,Nonce", "Nonce");
plist_dict_copy_bool(request, parameters, "BMU,ProductionMode", "ProductionMode");
plist_dict_copy_uint(request, parameters, "BMU,UniqueID", "UniqueID");
char *comp_name = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (iter) {
node = NULL;
comp_name = NULL;
plist_dict_next_item(manifest_node, iter, &comp_name, &node);
if (comp_name == NULL) {
node = NULL;
break;
}
if (strncmp(comp_name, "BMU,", 4) == 0) {
plist_t manifest_entry = plist_copy(node);
plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules");
if (rules) {
debug("DEBUG: Applying restore request rules for entry %s\n", comp_name);
tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules);
}
if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) {
debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name);
plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0));
}
plist_dict_remove_item(manifest_entry, "Info");
plist_dict_set_item(request, comp_name, manifest_entry);
}
free(comp_name);
}
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_tcon_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t node = NULL;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
plist_dict_set_item(request, "@Baobab,Ticket", plist_new_bool(1));
plist_dict_copy_uint(request, parameters, "Baobab,BoardID", NULL);
plist_dict_copy_uint(request, parameters, "Baobab,ChipID", NULL);
plist_dict_copy_data(request, parameters, "Baobab,ECID", NULL);
plist_dict_copy_uint(request, parameters, "Baobab,Life", NULL);
plist_dict_copy_uint(request, parameters, "Baobab,ManifestEpoch", NULL);
plist_dict_copy_bool(request, parameters, "Baobab,ProductionMode", NULL);
plist_dict_copy_uint(request, parameters, "Baobab,SecurityDomain", NULL);
plist_dict_copy_data(request, parameters, "Baobab,UpdateNonce", NULL);
uint8_t isprod = plist_dict_get_bool(parameters, "Baobab,ProductionMode");
char *comp_name = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (iter) {
node = NULL;
comp_name = NULL;
plist_dict_next_item(manifest_node, iter, &comp_name, &node);
if (comp_name == NULL) {
node = NULL;
break;
}
if (strncmp(comp_name, "Baobab,", 7) == 0) {
plist_t manifest_entry = plist_copy(node);
plist_dict_remove_item(manifest_entry, "Info");
plist_dict_set_item(manifest_entry, "EPRO", plist_new_bool(isprod));
plist_dict_set_item(request, comp_name, manifest_entry);
}
free(comp_name);
}
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_timer_tags(plist_t request, plist_t parameters, plist_t overrides)
{
plist_t node = NULL;
uint32_t tag = 0;
plist_t manifest_node = plist_dict_get_item(parameters, "Manifest");
if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) {
error("ERROR: %s: Unable to get restore manifest from parameters\n", __func__);
return -1;
}
plist_dict_set_item(request, "@BBTicket", plist_new_bool(1));
node = plist_dict_get_item(parameters, "TicketName");
if (!node) {
error("ERROR: %s: Missing TicketName\n", __func__);
return -1;
}
char key[64];
snprintf(key, sizeof(key), "@%s", plist_get_string_ptr(node, NULL));
plist_dict_set_item(request, key, plist_new_bool(1));
tag = (uint32_t)plist_dict_get_uint(parameters, "TagNumber");
snprintf(key, sizeof(key), "Timer,BoardID,%u", tag);
plist_dict_copy_uint(request, parameters, key, NULL);
snprintf(key, sizeof(key), "Timer,ChipID,%u", tag);
plist_dict_copy_uint(request, parameters, key, NULL);
snprintf(key, sizeof(key), "Timer,SecurityDomain,%u", tag);
plist_dict_copy_uint(request, parameters, key, NULL);
snprintf(key, sizeof(key), "Timer,SecurityMode,%u", tag);
plist_dict_copy_bool(request, parameters, key, NULL);
snprintf(key, sizeof(key), "Timer,ProductionMode,%u", tag);
plist_dict_copy_bool(request, parameters, key, NULL);
snprintf(key, sizeof(key), "Timer,ECID,%u", tag);
plist_dict_copy_uint(request, parameters, key, NULL);
snprintf(key, sizeof(key), "Timer,Nonce,%u", tag);
plist_dict_copy_data(request, parameters, key, NULL);
char *comp_name = NULL;
plist_dict_iter iter = NULL;
plist_dict_new_iter(manifest_node, &iter);
while (iter) {
node = NULL;
comp_name = NULL;
plist_dict_next_item(manifest_node, iter, &comp_name, &node);
if (comp_name == NULL) {
node = NULL;
break;
}
if (!strncmp(comp_name, "Timer,", 6)) {
plist_t manifest_entry = plist_copy(node);
plist_t rules = plist_access_path(manifest_entry, 2, "Info", "RestoreRequestRules");
if (rules) {
debug("DEBUG: Applying restore request rules for entry %s\n", comp_name);
tss_entry_apply_restore_request_rules(manifest_entry, parameters, rules);
}
if (plist_dict_get_bool(manifest_entry, "Trusted") && !plist_dict_get_item(manifest_entry, "Digest")) {
debug("DEBUG: No Digest data, using empty value for entry %s\n", comp_name);
plist_dict_set_item(manifest_entry, "Digest", plist_new_data(NULL, 0));
}
plist_dict_remove_item(manifest_entry, "Info");
plist_dict_set_item(request, comp_name, manifest_entry);
}
free(comp_name);
}
free(iter);
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
int tss_request_add_cryptex_tags(plist_t request, plist_t parameters, plist_t overrides)
{
tss_request_add_common_tags(request, parameters, NULL);
if (plist_dict_get_item(parameters, "Ap,LocalPolicy")) {
tss_request_add_local_policy_tags(request, parameters);
plist_dict_copy_data(request, parameters, "Ap,NextStageCryptex1IM4MHash", NULL);
} else {
plist_dict_set_item(request, "@Cryptex1,Ticket", plist_new_bool(1));
plist_dict_copy_bool(request, parameters, "ApSecurityMode", NULL);
plist_dict_copy_bool(request, parameters, "ApProductionMode", NULL);
plist_dict_iter iter = NULL;
plist_dict_new_iter(parameters, &iter);
plist_t value = NULL;
while (1) {
char *key = NULL;
plist_dict_next_item(parameters, iter, &key, &value);
if (key == NULL)
break;
if (strncmp(key, "Cryptex1", 8) == 0) {
plist_dict_set_item(request, key, plist_copy(value));
}
free(key);
}
}
if (overrides) {
plist_dict_merge(&request, overrides);
}
return 0;
}
static size_t tss_write_callback(char* data, size_t size, size_t nmemb, tss_response* response)
{
size_t total = size * nmemb;
if (total != 0) {
response->content = realloc(response->content, response->length + total + 1);
memcpy(response->content + response->length, data, total);
response->content[response->length + total] = '\0';
response->length += total;
}
return total;
}
plist_t tss_request_send(plist_t tss_request, const char* server_url_string)
{
debug_plist(tss_request);
char* request = NULL;
int status_code = -1;
int retry = 0;
int max_retries = 15;
unsigned int size = 0;
char curl_error_message[CURL_ERROR_SIZE];
const char* urls[6] = {
"https://gs.apple.com/TSS/controller?action=2",
"https://gs.apple.com.akadns.net/TSS/controller?action=2",
"https://gs.apple.com.v.aaplimg.com/TSS/controller?action=2",
"http://gs.apple.com/TSS/controller?action=2",
"http://gs.apple.com.akadns.net/TSS/controller?action=2",
"http://gs.apple.com.v.aaplimg.com/TSS/controller?action=2"
};
plist_to_xml(tss_request, &request, &size);
tss_response* response = NULL;
memset(curl_error_message, '\0', CURL_ERROR_SIZE);
while (retry++ < max_retries) {
response = NULL;
CURL* handle = curl_easy_init();
if (handle == NULL) {
break;
}
struct curl_slist* header = NULL;
header = curl_slist_append(header, "Cache-Control: no-cache");
header = curl_slist_append(header, "Content-type: text/xml; charset=\"utf-8\"");
header = curl_slist_append(header, "Expect:");
response = malloc(sizeof(tss_response));
if (response == NULL) {
fprintf(stderr, "Unable to allocate sufficient memory\n");
return NULL;
}
response->length = 0;
response->content = malloc(1);
response->content[0] = '\0';
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&tss_write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request);
curl_easy_setopt(handle, CURLOPT_USERAGENT, USER_AGENT_STRING);
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, (long)strlen(request));
if (server_url_string) {
curl_easy_setopt(handle, CURLOPT_URL, server_url_string);
debug("Request URL set to %s\n", server_url_string);
} else {
int url_index = (retry - 1) % 6;
curl_easy_setopt(handle, CURLOPT_URL, urls[url_index]);
debug("Request URL set to %s\n", urls[url_index]);
}
if (debug_level >= 2) {
curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
}
debug("Sending TSS request attempt %d... ", retry);
curl_easy_perform(handle);
curl_slist_free_all(header);
curl_easy_cleanup(handle);
if (strstr(response->content, "MESSAGE=SUCCESS")) {
status_code = 0;
debug("response successfully received\n");
break;
}
if (response->length > 0) {
error("TSS server returned: %s\n", response->content);
}
char* status = strstr(response->content, "STATUS=");
if (status) {
sscanf(status+7, "%d&%*s", &status_code);
}
if (status_code == -1) {
error("%s\n", curl_error_message);
free(response->content);
free(response);
response = NULL;
sleep(2);
continue;
} else if (status_code == 8) {
break;
} else if (status_code == 49) {
break;
} else if (status_code == 69 || status_code == 94) {
break;
} else if (status_code == 100) {
break;
} else if (status_code == 126) {
break;
} else {
error("ERROR: tss_send_request: Unhandled status code %d\n", status_code);
break;
}
}
if (status_code != 0) {
if (response && strstr(response->content, "MESSAGE=") != NULL) {
char* message = strstr(response->content, "MESSAGE=") + strlen("MESSAGE=");
error("ERROR: TSS request failed (status=%d, message=%s)\n", status_code, message);
} else {
error("ERROR: TSS request failed: %s (status=%d)\n", curl_error_message, status_code);
}
free(request);
if (response) free(response->content);
if (response) free(response);
return NULL;
}
char* tss_data = strstr(response->content, "<?xml");
if (tss_data == NULL) {
error("ERROR: Incorrectly formatted TSS response\n");
free(request);
free(response->content);
free(response);
return NULL;
}
uint32_t tss_size = 0;
plist_t tss_resp = NULL;
tss_size = response->length - (tss_data - response->content);
plist_from_xml(tss_data, tss_size, &tss_resp);
free(response->content);
free(response);
debug_plist(tss_resp);
free(request);
return tss_resp;
}
static int tss_response_get_data_by_key(plist_t response, const char* name, unsigned char** buffer, unsigned int* length)
{
plist_t node = plist_dict_get_item(response, name);
if (!node || plist_get_node_type(node) != PLIST_DATA) {
debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, name);
return -1;
}
char* data = NULL;
uint64_t len = 0;
plist_get_data_val(node, &data, &len);
if (data) {
*length = (unsigned int)len;
*buffer = (unsigned char*)data;
return 0;
} else {
error("ERROR: Unable to get %s data from TSS response\n", name);
return -1;
}
}
int tss_response_get_ap_img4_ticket(plist_t response, unsigned char** ticket, unsigned int* length)
{
return tss_response_get_data_by_key(response, "ApImg4Ticket", ticket, length);
}
int tss_response_get_ap_ticket(plist_t response, unsigned char** ticket, unsigned int* length)
{
return tss_response_get_data_by_key(response, "APTicket", ticket, length);
}
int tss_response_get_baseband_ticket(plist_t response, unsigned char** ticket, unsigned int* length)
{
return tss_response_get_data_by_key(response, "BBTicket", ticket, length);
}
int tss_response_get_path_by_entry(plist_t response, const char* entry, char** path)
{
char* path_string = NULL;
plist_t path_node = NULL;
plist_t entry_node = NULL;
*path = NULL;
entry_node = plist_dict_get_item(response, entry);
if (!entry_node || plist_get_node_type(entry_node) != PLIST_DICT) {
debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, entry);
return -1;
}
path_node = plist_dict_get_item(entry_node, "Path");
if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) {
debug("NOTE: Unable to find %s path in TSS entry\n", entry);
return -1;
}
plist_get_string_val(path_node, &path_string);
*path = path_string;
return 0;
}
int tss_response_get_blob_by_path(plist_t tss, const char* path, unsigned char** blob)
{
uint32_t i = 0;
uint32_t tss_size = 0;
uint64_t blob_size = 0;
char* entry_key = NULL;
char* blob_data = NULL;
char* entry_path = NULL;
plist_t tss_entry = NULL;
plist_t blob_node = NULL;
plist_t path_node = NULL;
plist_dict_iter iter = NULL;
*blob = NULL;
plist_dict_new_iter(tss, &iter);
tss_size = plist_dict_get_size(tss);
for (i = 0; i < tss_size; i++) {
plist_dict_next_item(tss, iter, &entry_key, &tss_entry);
if (entry_key == NULL)
break;
if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) {
continue;
}
path_node = plist_dict_get_item(tss_entry, "Path");
if (!path_node || plist_get_node_type(path_node) != PLIST_STRING) {
error("ERROR: Unable to find TSS path node in entry %s\n", entry_key);
free(iter);
return -1;
}
plist_get_string_val(path_node, &entry_path);
if (strcmp(path, entry_path) == 0) {
blob_node = plist_dict_get_item(tss_entry, "Blob");
if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) {
error("ERROR: Unable to find TSS blob node in entry %s\n", entry_key);
free(iter);
return -1;
}
plist_get_data_val(blob_node, &blob_data, &blob_size);
break;
}
free(entry_key);
}
free(iter);
if (blob_data == NULL || blob_size <= 0) {
return -1;
}
*blob = (unsigned char*)blob_data;
return 0;
}
int tss_response_get_blob_by_entry(plist_t response, const char* entry, unsigned char** blob)
{
uint64_t blob_size = 0;
char* blob_data = NULL;
plist_t blob_node = NULL;
plist_t tss_entry = NULL;
*blob = NULL;
tss_entry = plist_dict_get_item(response, entry);
if (!tss_entry || plist_get_node_type(tss_entry) != PLIST_DICT) {
debug("DEBUG: %s: No entry '%s' in TSS response\n", __func__, entry);
return -1;
}
blob_node = plist_dict_get_item(tss_entry, "Blob");
if (!blob_node || plist_get_node_type(blob_node) != PLIST_DATA) {
error("ERROR: Unable to find blob in %s entry\n", entry);
return -1;
}
plist_get_data_val(blob_node, &blob_data, &blob_size);
*blob = (unsigned char*)blob_data;
return 0;
}
void tss_set_debug_level(int level)
{
debug_level = level;
}