X509* SDMMD__decode_certificate(CFDataRef cert) {
	X509* result = NULL;
	if (cert) {
		BIO *newBIO = SDMMD__create_bio_from_data(cert);
		if (newBIO == 0) {
			printf("_decode_certificate: Could not create BIO from CFData.\n");
		} else {
			result = PEM_read_bio_X509(newBIO, 0x0, 0x0, 0x0);
			if (result == NULL) {
				printf("_decode_certificate: PEM_read_bio_X509 failed.\n");
			}
			BIO_free(newBIO);
		}
		
	}
	return result;
}
CFMutableDictionaryRef SDMMD__CreatePairingMaterial(CFDataRef devicePubkey)
{
	CFMutableDictionaryRef record = NULL;
	RSA *rsaBIOData = NULL;
	BIO *deviceBIO = SDMMD__create_bio_from_data(devicePubkey);
	if (deviceBIO) {
		PEM_read_bio_RSAPublicKey(deviceBIO, &rsaBIOData, NULL, NULL);
		BIO_free(deviceBIO);
	}
	else {
		printf("Could not decode device public key\\n");
	}

	RSA *rootKeyPair = RSA_generate_key(2048, 65537, NULL, NULL);
	if (!rootKeyPair) {
		printf("Could not allocate root key pair\n");
	}

	RSA *hostKeyPair = RSA_generate_key(2048, 65537, NULL, NULL);
	if (!hostKeyPair) {
		printf("Could not allocate host key pair\n");
	}

	sdmmd_return_t result = kAMDSuccess;

	EVP_PKEY *rootEVP = EVP_PKEY_new();
	if (!rootEVP) {
		printf("Could not allocate root EVP key\\n");
	}
	else {
		result = EVP_PKEY_assign(rootEVP, EVP_CTRL_RAND_KEY, PtrCast(rootKeyPair, char *));
		if (!result) {
			printf("Could not assign root key pair\n");
		}
	}

	EVP_PKEY *hostEVP = EVP_PKEY_new();
	if (!hostEVP) {
		printf("Could not allocate host EVP key\\n");
	}
	else {
		result = EVP_PKEY_assign(hostEVP, EVP_CTRL_RAND_KEY, PtrCast(hostKeyPair, char *));
		if (!result) {
			printf("Could not assign host key pair\n");
		}
	}

	EVP_PKEY *deviceEVP = EVP_PKEY_new();
	if (!deviceEVP) {
		printf("Could not allocate device EVP key\\n");
	}
	else {
		result = EVP_PKEY_assign(deviceEVP, EVP_CTRL_RAND_KEY, PtrCast(rsaBIOData, char *));
		if (!result) {
			printf("Could not assign device key pair\n");
		}
	}

	X509 *rootX509 = X509_new();
	if (!rootX509) {
		printf("Could not create root X509\\n");
	}
	else {
		X509_set_pubkey(rootX509, rootEVP);
		X509_set_version(rootX509, 2);

		ASN1_INTEGER *rootSerial = X509_get_serialNumber(rootX509);
		ASN1_INTEGER_set(rootSerial, 0);

		ASN1_TIME *rootAsn1time = ASN1_TIME_new();
		ASN1_TIME_set(rootAsn1time, 0);
		X509_set_notBefore(rootX509, rootAsn1time);
		ASN1_TIME_set(rootAsn1time, 0x12cc0300); // 60 sec * 60 minutes * 24 hours * 365 days * 10 years
		X509_set_notAfter(rootX509, rootAsn1time);
		ASN1_TIME_free(rootAsn1time);

		SDMMD__add_ext(rootX509, NID_basic_constraints, "critical,CA:TRUE");
		SDMMD__add_ext(rootX509, NID_subject_key_identifier, "hash");

		result = X509_sign(rootX509, rootEVP, EVP_sha1());
		if (!result) {
			printf("Could not sign root cert\\n");
		}
	}

	X509 *hostX509 = X509_new();
	if (!hostX509) {
		printf("Could not create host X509\\n");
	}
	else {
		X509_set_pubkey(hostX509, hostEVP);
		X509_set_version(hostX509, 2);

		ASN1_INTEGER *hostSerial = X509_get_serialNumber(hostX509);
		ASN1_INTEGER_set(hostSerial, 0);

		ASN1_TIME *hostAsn1time = ASN1_TIME_new();
		ASN1_TIME_set(hostAsn1time, 0);
		X509_set_notBefore(hostX509, hostAsn1time);
		ASN1_TIME_set(hostAsn1time, 0x12cc0300); // 60 sec * 60 minutes * 24 hours * 365 days * 10 years
		X509_set_notAfter(hostX509, hostAsn1time);
		ASN1_TIME_free(hostAsn1time);

		SDMMD__add_ext(hostX509, NID_basic_constraints, "critical,CA:FALSE");
		SDMMD__add_ext(hostX509, NID_subject_key_identifier, "hash");
		SDMMD__add_ext(hostX509, NID_key_usage, "critical,digitalSignature,keyEncipherment");

		result = X509_sign(hostX509, rootEVP, EVP_sha1());
		if (!result) {
			printf("Could not sign host cert\\n");
		}
	}

	X509 *deviceX509 = X509_new();
	if (!deviceX509) {
		printf("Could not create device X509\\n");
	}
	else {
		X509_set_pubkey(deviceX509, deviceEVP);
		X509_set_version(deviceX509, 2);

		ASN1_INTEGER *deviceSerial = X509_get_serialNumber(deviceX509);
		ASN1_INTEGER_set(deviceSerial, 0);

		ASN1_TIME *deviceAsn1time = ASN1_TIME_new();
		ASN1_TIME_set(deviceAsn1time, 0);
		X509_set_notBefore(deviceX509, deviceAsn1time);
		ASN1_TIME_set(deviceAsn1time, 0x12cc0300); // 60 sec * 60 minutes * 24 hours * 365 days * 10 years
		X509_set_notAfter(deviceX509, deviceAsn1time);
		ASN1_TIME_free(deviceAsn1time);

		SDMMD__add_ext(deviceX509, NID_basic_constraints, "critical,CA:FALSE");
		SDMMD__add_ext(deviceX509, NID_subject_key_identifier, "hash");
		SDMMD__add_ext(deviceX509, NID_key_usage, "critical,digitalSignature,keyEncipherment");

		result = X509_sign(deviceX509, rootEVP, EVP_sha1());
		if (!result) {
			printf("Could not sign device cert\\n");
		}
	}

	CFDataRef rootCert = SDMMD_CreateDataFromX509Certificate(rootX509);
	CFDataRef hostCert = SDMMD_CreateDataFromX509Certificate(hostX509);
	CFDataRef deviceCert = SDMMD_CreateDataFromX509Certificate(deviceX509);
	CFDataRef rootPrivKey = SDMMD_CreateDataFromPrivateKey(rootEVP);
	CFDataRef hostPrivKey = SDMMD_CreateDataFromPrivateKey(hostEVP);
	CFStringRef hostId = SDMMD_CreateUUID();

	record = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
	if (record) {
		CFDictionarySetValue(record, CFSTR("RootCertificate"), rootCert);

		CFDictionarySetValue(record, CFSTR("HostCertificate"), hostCert);

		CFDictionarySetValue(record, CFSTR("DeviceCertificate"), deviceCert);

		CFDictionarySetValue(record, CFSTR("RootPrivateKey"), rootPrivKey);

		CFDictionarySetValue(record, CFSTR("HostPrivateKey"), hostPrivKey);

		CFDictionarySetValue(record, CFSTR("HostID"), hostId);
	}
	CFSafeRelease(rootCert);
	CFSafeRelease(hostCert);
	CFSafeRelease(deviceCert);
	CFSafeRelease(rootPrivKey);
	CFSafeRelease(hostPrivKey);
	CFSafeRelease(hostId);

	Safe(EVP_PKEY_free, rootEVP);
	Safe(EVP_PKEY_free, hostEVP);
	Safe(EVP_PKEY_free, deviceEVP);
	Safe(X509_free, rootX509);
	Safe(X509_free, hostX509);
	Safe(X509_free, deviceX509);

	return record;
}
SSL* SDMMD_lockssl_handshake(SDMMD_lockdown_conn *lockdown_conn, CFTypeRef hostCert, CFTypeRef deviceCert, CFTypeRef hostPrivKey, uint32_t num) {
	SSL *ssl = NULL;
	SSL_CTX *sslCTX = NULL;
	sdmmd_return_t result = 0x0;
	BIO_METHOD *bioMethod = BIO_s_socket();
	BIO *bioSocket = BIO_new(bioMethod);
	if (bioSocket) {
		BIO_set_fd(bioSocket, lockdown_conn->connection, 0);
		X509 *cert = SDMMD__decode_certificate(hostCert);
		if (cert == 0) {
			printf("_create_ssl_context: Could not certificate.\n");
		}
		RSA *rsa = NULL;
		BIO *dataBIO = SDMMD__create_bio_from_data(hostPrivKey);
		if (dataBIO == 0) {
			printf("_create_ssl_context: Could not decode host private key.\n");
			if (cert) {
				X509_free(cert);
				result = 0x0;
			}
		} else {
			PEM_read_bio_RSAPrivateKey(dataBIO, &rsa, 0x0, 0x0);
			BIO_free(dataBIO);
			if (rsa) {
				if (hostCert) {
					sslCTX = SSL_CTX_new(SSLv3_method());
					if (sslCTX) {
						result = SSL_CTX_use_certificate(sslCTX, cert);
						if (result == 0)
							printf("_create_ssl_context: Could not set certificate.\n");
						result = SSL_CTX_use_RSAPrivateKey(sslCTX, rsa);
						if (result == 0)
							printf("_create_ssl_context: Could not set private key.\n");
					} else {
						printf("_create_ssl_context: Could not create SSLv3 context.\n");
					}
				}
				RSA_free(rsa);
				if (cert)
					X509_free(cert);
				if (sslCTX) {
					ssl = SSL_new(sslCTX);
					if (ssl) {
						if (num) {
							SSL_set_connect_state(ssl);
						} else {
							SSL_set_accept_state(ssl);
						}
						SSL_set_verify(ssl, 0x3, SDMMD__ssl_verify_callback);
						SSL_set_verify_depth(ssl, 0x0);
						SSL_set_bio(ssl, bioSocket, bioSocket);
						SSL_set_ex_data(ssl, SDMMobileDevice->peer_certificate_data_index, (void*)deviceCert);
						result = SSL_do_handshake(ssl);
						if (result == 1) {
							SSL_CTX_free(sslCTX);
						} else {
							uint32_t err = SSL_get_error(ssl, result);
							if (err) {
								char *reason = SDMMD_ssl_strerror(ssl, err);
								printf("lockssl_handshake: SSL handshake fatal lower level error %d: %s.\n", err, reason);
							} else {
								char *reason = SDMMD_ssl_strerror(ssl, 0x0);
								printf("lockssl_handshake: SSL handshake controlled failure %d: %s.\n", err, reason);
							}
							SSL_free(ssl);
						}
					} else {
						printf("_create_ssl: Could not create SSL thing.\n");
					}
				}
			} else {
				printf("_create_ssl_context: Could not decode private key.\n");
				if (cert) {
					X509_free(cert);
					result = 0x0;
				}
			}
		}
	} else {
		printf("lockssl_handshake: Could not create SSL bio.\n");
	}
	return ssl;
}