<commit_before>/*
 * lockdown.c
 * libiphone built-in lockdownd client
 * 
 * Copyright (c) 2008 <NAME>. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 */

#include "usbmux.h"
#include "iphone.h"
#include "lockdown.h"
#include "userpref.h"
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libtasn1.h>
#include <gnutls/x509.h>

const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
	{"PKCS1", 536872976, 0},
	{0, 1073741836, 0},
	{"RSAPublicKey", 536870917, 0},
	{"modulus", 1073741827, 0},
	{"publicExponent", 3, 0},
	{0, 0, 0}
};

int get_rand(int min, int max)
{
	int retval = (rand() % (max - min)) + min;
	return retval;
}

/** Generates a valid HostID (which is actually a UUID).
 *
 * @param A null terminated string containing a valid HostID.
 */
char *lockdownd_generate_hostid()
{
	char *hostid = (char *) malloc(sizeof(char) * 37);	// HostID's are just UUID's, and UUID's are 36 characters long
	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) {
			hostid[i] = '-';
			continue;
		} else {
			hostid[i] = chars[get_rand(0, 16)];
		}
	}
	hostid[36] = '\0';			// make it a real string
	return hostid;
}

/** Creates a lockdownd client for the give iPhone.
 *
 * @param phone The iPhone to create a lockdownd client for
 *
 * @return The lockdownd client.
 */
iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone)
{
	if (!phone)
		return NULL;
	iphone_lckd_client_t control = (iphone_lckd_client_t) malloc(sizeof(struct iphone_lckd_client_int));

	if (IPHONE_E_SUCCESS != iphone_mux_new_client(phone, 0x0a00, 0xf27e, &control->connection)) {
		free(control);
		return NULL;
	}

	control->ssl_session = (gnutls_session_t *) malloc(sizeof(gnutls_session_t));
	control->in_SSL = 0;
	control->gtls_buffer_hack_len = 0;
	return control;
}


/** Closes the lockdownd client and does the necessary housekeeping.
 *
 * @param control The lockdown client
 */
iphone_error_t iphone_lckd_free_client(iphone_lckd_client_t client)
{
	if (!client)
		return IPHONE_E_INVALID_ARG;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	if (client->connection) {
		ret = iphone_mux_free_client(client->connection);
	}

	if (client->ssl_session)
		gnutls_deinit(*client->ssl_session);
	free(client->ssl_session);
	free(client);
	return ret;
}

/** Polls the iPhone for lockdownd data.
 *
 * @param control The lockdownd client
 * @param dump_data The pointer to the location of the buffer in which to store
 *                  the received data
 *
 * @return The number of bytes received
 */
iphone_error_t iphone_lckd_recv(iphone_lckd_client_t client, char **dump_data, uint32_t * recv_bytes)
{
	if (!client || !dump_data || !recv_bytes)
		return IPHONE_E_INVALID_ARG;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
	char *receive;
	uint32_t datalen = 0, bytes = 0;

	if (!client->in_SSL)
		ret = iphone_mux_recv(client->connection, (char *) &datalen, sizeof(datalen), &bytes);
	else {
		bytes = gnutls_record_recv(*client->ssl_session, &datalen, sizeof(datalen));
		if (bytes > 0)
			ret = IPHONE_E_SUCCESS;
	}
	datalen = ntohl(datalen);

	receive = (char *) malloc(sizeof(char) * datalen);
	if (!client->in_SSL)
		ret = iphone_mux_recv(client->connection, receive, datalen, &bytes);
	else {
		bytes = gnutls_record_recv(*client->ssl_session, receive, datalen);
		if (bytes > 0)
			ret = IPHONE_E_SUCCESS;
	}
	*dump_data = receive;
	*recv_bytes = bytes;
	return ret;
}

/** Sends lockdownd data to the iPhone
 * 
 * @note This function is low-level and should only be used if you need to send
 *        a new type of message.
 *
 * @param control The lockdownd client
 * @param raw_data The null terminated string buffer to send
 * @param length The length of data to send
 *
 * @return The number of bytes sent
 */
iphone_error_t iphone_lckd_send(iphone_lckd_client_t client, char *raw_data, uint32_t length, uint32_t * sent_bytes)
{
	if (!client || !raw_data || length == 0 || !sent_bytes)
		return IPHONE_E_INVALID_ARG;
	char *real_query;
	int bytes;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	real_query = (char *) malloc(sizeof(char) * (length + 4));
	length = htonl(length);
	memcpy(real_query, &length, sizeof(length));
	memcpy(real_query + 4, raw_data, ntohl(length));
	log_debug_msg("lockdownd_send(): made the query, sending it along\n");
	dump_debug_buffer("grpkt", real_query, ntohl(length) + 4);

	if (!client->in_SSL)
		ret = iphone_mux_send(client->connection, real_query, ntohl(length) + sizeof(length), &bytes);
	else {
		gnutls_record_send(*client->ssl_session, real_query, ntohl(length) + sizeof(length));
		ret = IPHONE_E_SUCCESS;
	}
	log_debug_msg("lockdownd_send(): sent it!\n");
	free(real_query);
	*sent_bytes = bytes;
	return ret;
}

/** Initiates the handshake for the lockdown session. Part of the lockdownd handshake.
 * 
 * @note You most likely want lockdownd_init unless you are doing something special.
 *
 * @param control The lockdownd client
 *
 * @return 1 on success and 0 on failure.
 */
iphone_error_t lockdownd_hello(iphone_lckd_client_t control)
{
	if (!control)
		return IPHONE_E_INVALID_ARG;

	int bytes = 0, i = 0;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	plist_t plist = NULL;
	plist_new_plist(&plist);

	dict_t dict = NULL;
	plist_new_dict_in_plist(plist, &dict);

	plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "QueryType");

	log_debug_msg("lockdownd_hello() called\n");
	char *XML_content = NULL;
	uint32_t length = 0;

	plist_to_xml(plist, &XML_content, &length);
	log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content);
	ret = iphone_lckd_send(control, XML_content, length, &bytes);

	xmlFree(XML_content);
	XML_content = NULL;
	plist_free(plist);
	plist = NULL;

	ret = iphone_lckd_recv(control, &XML_content, &bytes);
	log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content);
	xml_to_plist(XML_content, bytes, &plist);

	if (!plist)
		return IPHONE_E_PLIST_ERROR;

	plist_t query_node = find_query_node(plist, "Request", "QueryType");
	plist_t result_node = g_node_next_sibling(query_node);
	plist_t value_node = g_node_next_sibling(result_node);

	plist_type result_type;
	plist_type value_type;

	char *result_value = NULL;
	char *value_value = NULL;

	get_type_and_value(result_node, &result_type, (void *) (&result_value));
	get_type_and_value(value_node, &value_type, (void *) (&value_value));

	if (result_type == PLIST_KEY &&
		value_type == PLIST_STRING && !strcmp(result_value, "Result") && !strcmp(value_value, "Success")) {
		log_debug_msg("lockdownd_hello(): success\n");
		ret = IPHONE_E_SUCCESS;
	}

	return ret;
}

/** Generic function to handle simple (key, value) requests.
 *
 * @param control an initialized lockdownd client.
 * @param key the key to request
 * @param value a pointer to the requested value
 *
 * @return IPHONE_E_SUCCESS on success.
 */
iphone_error_t lockdownd_generic_get_value(iphone_lckd_client_t control, char *req_key, char *req_string, char **value)
{
	if (!control || !req_key || !value || (value && *value))
		return IPHONE_E_INVALID_ARG;
	xmlDocPtr plist = new_plist();
	xmlNode *dict = NULL;
	xmlNode *key = NULL;;
	char **dictionary = NULL;
	int bytes = 0, i = 0;
	char *XML_content = NULL;
	uint32_t length = 0;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	/* Setup DevicePublicKey request plist */
	dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
	key = add_key_str_dict_element(plist, dict, req_key, req_string, 1);
	key = add_key_str_dict_element(plist, dict, "Request", "GetValue", 1);
	xmlDocDumpMemory(plist, (xmlChar **) & XML_content, &length);

	/* send to iPhone */
	ret = iphone_lckd_send(control, XML_content, length, &bytes);

	xmlFree(XML_content);
	xmlFreeDoc(plist);
	plist = NULL;

	if (ret != IPHONE_E_SUCCESS)
		return ret;

	/* Now get iPhone's answer */
	ret = iphone_lckd_recv(control, &XML_content, &bytes);

	if (ret != IPHONE_E_SUCCESS)
		return ret;

	plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0);
	if (!plist)
		return IPHONE_E_PLIST_ERROR;
	dict = xmlDocGetRootElement(plist);
	for (dict = dict->children; dict; dict = dict->next) {
		if (!xmlStrcmp(dict->name, "dict"))
			break;
	}
	if (!dict)
		return IPHONE_E_DICT_ERROR;

	/* Parse xml to check success and to find public key */
	dictionary = read_dict_element_strings(dict);
	xmlFreeDoc(plist);
	free(XML_content);

	int success = 0;
	for (i = 0; dictionary[i]; i += 2) {
		if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i + 1], "Success")) {
			success = 1;
		}
		if (!strcmp(dictionary[i], "Value")) {
			*value = strdup(dictionary[i + 1]);
		}
	}

	if (dictionary) {
		free_dictionary(dictionary);
		dictionary = NULL;
	}
	if (success)
		ret = IPHONE_E_SUCCESS;
	return ret;
}

/** Askes for the device's unique id. Part of the lockdownd handshake.
 *
 * @note You most likely want lockdownd_init unless you are doing something special.
 *
 * @return 1 on success and 0 on failure.
 */
iphone_error_t lockdownd_get_device_uid(iphone_lckd_client_t control, char **uid)
{
	return lockdownd_generic_get_value(control, "Key", "UniqueDeviceID", uid);
}

/** Askes for the device's public key. Part of the lockdownd handshake.
 *
 * @note You most likely want lockdownd_init unless you are doing something special.
 *
 * @return 1 on success and 0 on failure.
 */
iphone_error_t lockdownd_get_device_public_key(iphone_lckd_client_t control, char **public_key)
{
	return lockdownd_generic_get_value(control, "Key", "DevicePublicKey", public_key);
}

/** Completes the entire lockdownd handshake.
 *
 * @param phone The iPhone
 * @param lockdownd_client The pointer to the location of the lockdownd_client<commit_msg>migrate lockdownd_generic_get_value to new plisy API.<commit_after>/*
 * lockdown.c
 * libiphone built-in lockdownd client
 * 
 * Copyright (c) 2008 <NAME>. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
 */

#include "usbmux.h"
#include "iphone.h"
#include "lockdown.h"
#include "userpref.h"
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libtasn1.h>
#include <gnutls/x509.h>

const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
	{"PKCS1", 536872976, 0},
	{0, 1073741836, 0},
	{"RSAPublicKey", 536870917, 0},
	{"modulus", 1073741827, 0},
	{"publicExponent", 3, 0},
	{0, 0, 0}
};

int get_rand(int min, int max)
{
	int retval = (rand() % (max - min)) + min;
	return retval;
}

/** Generates a valid HostID (which is actually a UUID).
 *
 * @param A null terminated string containing a valid HostID.
 */
char *lockdownd_generate_hostid()
{
	char *hostid = (char *) malloc(sizeof(char) * 37);	// HostID's are just UUID's, and UUID's are 36 characters long
	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) {
			hostid[i] = '-';
			continue;
		} else {
			hostid[i] = chars[get_rand(0, 16)];
		}
	}
	hostid[36] = '\0';			// make it a real string
	return hostid;
}

/** Creates a lockdownd client for the give iPhone.
 *
 * @param phone The iPhone to create a lockdownd client for
 *
 * @return The lockdownd client.
 */
iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone)
{
	if (!phone)
		return NULL;
	iphone_lckd_client_t control = (iphone_lckd_client_t) malloc(sizeof(struct iphone_lckd_client_int));

	if (IPHONE_E_SUCCESS != iphone_mux_new_client(phone, 0x0a00, 0xf27e, &control->connection)) {
		free(control);
		return NULL;
	}

	control->ssl_session = (gnutls_session_t *) malloc(sizeof(gnutls_session_t));
	control->in_SSL = 0;
	control->gtls_buffer_hack_len = 0;
	return control;
}


/** Closes the lockdownd client and does the necessary housekeeping.
 *
 * @param control The lockdown client
 */
iphone_error_t iphone_lckd_free_client(iphone_lckd_client_t client)
{
	if (!client)
		return IPHONE_E_INVALID_ARG;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	if (client->connection) {
		ret = iphone_mux_free_client(client->connection);
	}

	if (client->ssl_session)
		gnutls_deinit(*client->ssl_session);
	free(client->ssl_session);
	free(client);
	return ret;
}

/** Polls the iPhone for lockdownd data.
 *
 * @param control The lockdownd client
 * @param dump_data The pointer to the location of the buffer in which to store
 *                  the received data
 *
 * @return The number of bytes received
 */
iphone_error_t iphone_lckd_recv(iphone_lckd_client_t client, char **dump_data, uint32_t * recv_bytes)
{
	if (!client || !dump_data || !recv_bytes)
		return IPHONE_E_INVALID_ARG;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
	char *receive;
	uint32_t datalen = 0, bytes = 0;

	if (!client->in_SSL)
		ret = iphone_mux_recv(client->connection, (char *) &datalen, sizeof(datalen), &bytes);
	else {
		bytes = gnutls_record_recv(*client->ssl_session, &datalen, sizeof(datalen));
		if (bytes > 0)
			ret = IPHONE_E_SUCCESS;
	}
	datalen = ntohl(datalen);

	receive = (char *) malloc(sizeof(char) * datalen);
	if (!client->in_SSL)
		ret = iphone_mux_recv(client->connection, receive, datalen, &bytes);
	else {
		bytes = gnutls_record_recv(*client->ssl_session, receive, datalen);
		if (bytes > 0)
			ret = IPHONE_E_SUCCESS;
	}
	*dump_data = receive;
	*recv_bytes = bytes;
	return ret;
}

/** Sends lockdownd data to the iPhone
 * 
 * @note This function is low-level and should only be used if you need to send
 *        a new type of message.
 *
 * @param control The lockdownd client
 * @param raw_data The null terminated string buffer to send
 * @param length The length of data to send
 *
 * @return The number of bytes sent
 */
iphone_error_t iphone_lckd_send(iphone_lckd_client_t client, char *raw_data, uint32_t length, uint32_t * sent_bytes)
{
	if (!client || !raw_data || length == 0 || !sent_bytes)
		return IPHONE_E_INVALID_ARG;
	char *real_query;
	int bytes;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	real_query = (char *) malloc(sizeof(char) * (length + 4));
	length = htonl(length);
	memcpy(real_query, &length, sizeof(length));
	memcpy(real_query + 4, raw_data, ntohl(length));
	log_debug_msg("lockdownd_send(): made the query, sending it along\n");
	dump_debug_buffer("grpkt", real_query, ntohl(length) + 4);

	if (!client->in_SSL)
		ret = iphone_mux_send(client->connection, real_query, ntohl(length) + sizeof(length), &bytes);
	else {
		gnutls_record_send(*client->ssl_session, real_query, ntohl(length) + sizeof(length));
		ret = IPHONE_E_SUCCESS;
	}
	log_debug_msg("lockdownd_send(): sent it!\n");
	free(real_query);
	*sent_bytes = bytes;
	return ret;
}

/** Initiates the handshake for the lockdown session. Part of the lockdownd handshake.
 * 
 * @note You most likely want lockdownd_init unless you are doing something special.
 *
 * @param control The lockdownd client
 *
 * @return 1 on success and 0 on failure.
 */
iphone_error_t lockdownd_hello(iphone_lckd_client_t control)
{
	if (!control)
		return IPHONE_E_INVALID_ARG;

	int bytes = 0, i = 0;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	plist_t plist = NULL;
	plist_new_plist(&plist);

	dict_t dict = NULL;
	plist_new_dict_in_plist(plist, &dict);

	plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "QueryType");

	log_debug_msg("lockdownd_hello() called\n");
	char *XML_content = NULL;
	uint32_t length = 0;

	plist_to_xml(plist, &XML_content, &length);
	log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content);
	ret = iphone_lckd_send(control, XML_content, length, &bytes);

	xmlFree(XML_content);
	XML_content = NULL;
	plist_free(plist);
	plist = NULL;

	ret = iphone_lckd_recv(control, &XML_content, &bytes);
	log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content);
	xml_to_plist(XML_content, bytes, &plist);

	if (!plist)
		return IPHONE_E_PLIST_ERROR;

	plist_t query_node = find_query_node(plist, "Request", "QueryType");
	plist_t result_node = g_node_next_sibling(query_node);
	plist_t value_node = g_node_next_sibling(result_node);

	plist_type result_type;
	plist_type value_type;

	char *result_value = NULL;
	char *value_value = NULL;

	get_type_and_value(result_node, &result_type, (void *) (&result_value));
	get_type_and_value(value_node, &value_type, (void *) (&value_value));

	if (result_type == PLIST_KEY &&
		value_type == PLIST_STRING && !strcmp(result_value, "Result") && !strcmp(value_value, "Success")) {
		log_debug_msg("lockdownd_hello(): success\n");
		ret = IPHONE_E_SUCCESS;
	}

	return ret;
}

/** Generic function to handle simple (key, value) requests.
 *
 * @param control an initialized lockdownd client.
 * @param key the key to request
 * @param value a pointer to the requested value
 *
 * @return IPHONE_E_SUCCESS on success.
 */
iphone_error_t lockdownd_generic_get_value(iphone_lckd_client_t control, char *req_key, char *req_string, char **value)
{
	if (!control || !req_key || !value || (value && *value))
		return IPHONE_E_INVALID_ARG;
	plist_t plist = NULL;
	dict_t dict = NULL;
	int bytes = 0, i = 0;
	char *XML_content = NULL;
	uint32_t length = 0;
	iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;

	/* Setup DevicePublicKey request plist */
	plist_new_plist(&plist);
	plist_new_dict_in_plist(plist, &dict);
	plist_add_dict_element(dict, req_key, PLIST_STRING, (void *) req_string);
	plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "GetValue");
	plist_to_xml(plist, &XML_content, &length);

	/* send to iPhone */
	log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content);
	ret = iphone_lckd_send(control, XML_content, length, &bytes);

	xmlFree(XML_content);
	XML_content = NULL;
	plist_free(plist);
	plist = NULL;

	if (ret != IPHONE_E_SUCCESS)
		return ret;

	/* Now get iPhone's answer */
	ret = iphone_lckd_recv(control, &XML_content, &bytes);
	log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content);

	if (ret != IPHONE_E_SUCCESS)
		return ret;

	xml_to_plist(XML_content, bytes, &plist);
	if (!plist)
		return IPHONE_E_PLIST_ERROR;

	plist_t query_node = find_query_node(plist, "Request", "GetValue");
	plist_t result_key_node = g_node_next_sibling(query_node);
	plist_t result_value_node = g_node_next_sibling(result_key_node);

	plist_type result_key_type;
	plist_type result_value_type;
	char *result_key = NULL;
	char *result_value = NULL;

	get_type_and_value(result_key_node, &result_key_type, (void *) (&result_key));
	get_type_and_value(result_value_node, &result_value_type, (void *) (&result_value));

	if (result_key_type == PLIST_KEY &&
		result_value_type == PLIST_STRING && !strcmp(result_key, "Result") && !strcmp(result_value, "Success")) {
		log_debug_msg("lockdownd_generic_get_value(): success\n");
		ret = IPHONE_E_SUCCESS;
	}

	if (ret != IPHONE_E_SUCCESS) {
		return IPHONE_E_DICT_ERROR;
	}

	plist_t value_key_node = g_node_next_sibling(result_key_node);
	plist_t value_value_node = g_node_next_sibling(value_key_node);
	plist_type value_key_type;
	plist_type value_value_type;
	char *value_key = NULL;
	char *value_value = NULL;

	get_type_and_value(value_key_node, &value_key_type, (void *) (&value_key));
	get_type_and_value(value_value_node, &value_value_type, (void *) (&value_value));

	if (value_key_type == PLIST_KEY && !strcmp(result_key, "Value")) {
		log_debug_msg("lockdownd_generic_get_value(): success\n");
		*value = value_value;
		ret = IPHONE_E_SUCCESS;
	}

	plist_free(plist);
	free(XML_content);
	return ret;
}

/** Askes for the device's unique id. Part of the lockdownd handshake.
 *
 * @note You most likely want lockdownd_init unless you are doing something special.
 *
 * @return 1 on success and 0 on failure.
 */
iphone_error_t lockdownd_get_device_uid(iphone_lckd_client_t control, char **uid)
{
	return lockdownd_generic_get_value(control, "Key", "UniqueDeviceID", uid);
}

/** Askes for the device's public key. Part of the lockdownd handshake.
 *
 * @note You most likely want lockdownd_init unless you are doing something special.
 *
 * @return 1 on success and 0 on failure.
 */
iphone_error_t lockdownd_get_device_public_key(iphone_lckd_client_t control, char **public_key)
{
	return lockdownd_generic_get_value(control, "Key", "DevicePublicKey", public_key);
}

/** Completes the entire lockdownd handshake.
 *
 * @param phone The iPhone
 * @param lockdownd_client The pointer to the location of the lockdownd_client
{'fraction_non_alphanumeric': 0.11949367088607595, 'fraction_numerical': 0.01380897583429229, 'mean_word_length': 4.886209699268491, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 27}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2918511', 'n_tokens_mistral': 8467, 'n_tokens_neox': 7883, 'n_words': 2099}