From 03d3d94d604eea77bb017c014123fba3d0f6e104 Mon Sep 17 00:00:00 2001 From: Connor Harrison Date: Mon, 29 Jul 2024 23:39:45 +0100 Subject: [PATCH 1/4] fix up --- lib/dnssd.c | 9 +++++++++ lib/httpd.c | 2 +- lib/raop.c | 25 ++++++++++++++----------- lib/raop_handlers.h | 7 ++++++- uxplay.cpp | 11 ++++++----- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/dnssd.c b/lib/dnssd.c index 07259375f..10f167d04 100644 --- a/lib/dnssd.c +++ b/lib/dnssd.c @@ -377,6 +377,8 @@ dnssd_register_airplay(dnssd_t *dnssd, unsigned short port) dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV); + return 0; + /* Register the service */ retval = dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0, dnssd->name, "_airplay._tcp", @@ -396,6 +398,13 @@ dnssd_get_airplay_txt(dnssd_t *dnssd, int *length) return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record); } +const char * +dnssd_get_raop_txt(dnssd_t *dnssd, int *length) +{ + *length = dnssd->TXTRecordGetLength(&dnssd->raop_record); + return dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record); +} + const char * dnssd_get_name(dnssd_t *dnssd, int *length) { diff --git a/lib/httpd.c b/lib/httpd.c index 7140e2e9b..83a288919 100644 --- a/lib/httpd.c +++ b/lib/httpd.c @@ -359,7 +359,7 @@ httpd_thread(void *arg) if (http_request_is_complete(connection->request)) { http_response_t *response = NULL; // Callback the received data to raop - if (logger_debug) { + if (logger_debug) { const char *method = http_request_get_method(connection->request); const char *url = http_request_get_url(connection->request); const char *protocol = http_request_get_protocol(connection->request); diff --git a/lib/raop.c b/lib/raop.c index af14baf7a..4c65736e3 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -170,24 +170,26 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { const char *protocol = http_request_get_protocol(request); const char *cseq = http_request_get_header(request, "CSeq"); + printf("Test"); if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) { + printf("Test2"); if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) { + printf("Test3"); char ipaddr[40]; utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr))); if (httpd_nohold(conn->raop->httpd)) { logger_log(conn->raop->logger, LOGGER_INFO, "\"nohold\" feature: switch to new connection request from %s", ipaddr); if (conn->raop->callbacks.video_reset) { - printf("**************************video_reset*************************\n"); + printf("**************************video_reset*************************\n"); conn->raop->callbacks.video_reset(conn->raop->callbacks.cls); - } - httpd_remove_known_connections(conn->raop->httpd); - + } + httpd_remove_known_connections(conn->raop->httpd); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr); *response = http_response_create(); http_response_init(*response, protocol, 409, "Conflict: Server is connected to another client"); goto finish; - } + } } httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP); conn->connection_type = CONNECTION_TYPE_RAOP; @@ -209,10 +211,10 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { } /* this rejects unsupported messages from _airplay._tcp for video streaming protocol*/ - if (!cseq) { - return; - } - + // if (!cseq) { + // return; + // } + // printf("Test4"); logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol); char *header_str= NULL; http_request_get_header_string(request, &header_str); @@ -255,8 +257,9 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url); raop_handler_t handler = NULL; - if (!strcmp(method, "GET") && !strcmp(url, "/info")) { + if (!strcmp(method, "GET") && strstr(url, "/info") != NULL) { handler = &raop_handler_info; + logger_log(conn->raop->logger, LOGGER_DEBUG, "Correct path taken!!!!!"); } else if (!strcmp(method, "POST") && !strcmp(url, "/pair-pin-start")) { handler = &raop_handler_pairpinstart; } else if (!strcmp(method, "POST") && !strcmp(url, "/pair-setup-pin")) { @@ -292,7 +295,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { } finish:; http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION); - http_response_add_header(*response, "CSeq", cseq); + // http_response_add_header(*response, "CSeq", cseq); http_response_finish(*response, response_data, response_datalen); int len; diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index 6493c3330..fee361912 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -34,7 +34,7 @@ raop_handler_info(raop_conn_t *conn, http_request_t *request, http_response_t *response, char **response_data, int *response_datalen) { - assert(conn->raop->dnssd); + // assert(conn->raop->dnssd); plist_t res_node = plist_new_dict(); @@ -59,6 +59,11 @@ raop_handler_info(raop_conn_t *conn, plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len); plist_dict_set_item(res_node, "txtAirPlay", txt_airplay_node); + int raop_txt_len = 0; + const char *raop_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &raop_txt_len); + plist_t txt_raop_node = plist_new_data(raop_txt, raop_txt_len); + plist_dict_set_item(res_node, "txtRAOP", txt_raop_node); + uint64_t features = dnssd_get_airplay_features(conn->raop->dnssd); plist_t features_node = plist_new_uint(features); plist_dict_set_item(res_node, "features", features_node); diff --git a/uxplay.cpp b/uxplay.cpp index ddf7af98a..f4c7d1e45 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -2161,11 +2161,12 @@ int main (int argc, char *argv[]) { stop_dnssd(); goto cleanup; } - if (register_dnssd()) { - stop_raop_server(); - stop_dnssd(); - goto cleanup; - } + register_dnssd(); + // if (register_dnssd()) { + // stop_raop_server(); + // stop_dnssd(); + // goto cleanup; + // } reconnect: compression_type = 0; close_window = new_window_closing_behavior; From 4e32c8121a43238168ee6be789ad404c818978b2 Mon Sep 17 00:00:00 2001 From: Connor Date: Fri, 30 Aug 2024 15:36:31 +0100 Subject: [PATCH 2/4] First working handshake commit --- lib/CMakeLists.txt | 4 + lib/http_request.c | 36 ++++++ lib/http_request.h | 2 + lib/httpd.c | 72 +++++++++++ lib/pairing.c | 25 +++- lib/pairing.h | 15 ++- lib/raop.c | 10 +- lib/raop_handlers.h | 114 ++++++++++++++-- lib/raop_rtp_mirror.c | 6 +- lib/srp.c | 295 +++++++++++++++++++++++++++++++++++++----- lib/srp.h | 34 ++++- uxplay.cpp | 12 +- 12 files changed, 549 insertions(+), 76 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 7167c94f8..7b8a8a252 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -163,3 +163,7 @@ if ( NOT APPLE ) target_include_directories( airplay PRIVATE ${DNSSD_INCLUDE_DIR} ) endif() endif() + +pkg_check_modules(SODIUM REQUIRED libsodium) +include_directories(${SODIUM_INCLUDE_DIRS}) +target_link_libraries(airplay ${SODIUM_LIBRARIES}) diff --git a/lib/http_request.c b/lib/http_request.c index 5a6343ad7..855aa92ad 100644 --- a/lib/http_request.c +++ b/lib/http_request.c @@ -39,6 +39,12 @@ struct http_request_s { int datalen; int complete; + + unsigned char *decryption_key; + int decryption_key_len; + + unsigned char *encryption_key; + int encryption_key_len; }; static int @@ -309,3 +315,33 @@ http_request_get_header_string(http_request_t *request, char **header_str) assert(p == &(str[len])); return len; } + +void +http_request_begin_encryption(http_request_t *request, unsigned char * decryption_key, int decryption_key_len, unsigned char * encryption_key, int encryption_key_len) { + assert(request); + + request->decryption_key = decryption_key; + request->decryption_key_len = decryption_key_len; + request->encryption_key = encryption_key; + request->encryption_key_len = encryption_key_len; +} + +int +http_request_get_encryption(http_request_t *request, unsigned char ** decryption_key, int *decryption_key_len, unsigned char ** encryption_key, int *encryption_key_len) { + assert(request); + + if (request->decryption_key_len > 0) { + *decryption_key = request->decryption_key; + *decryption_key_len = request->decryption_key_len; + *encryption_key = request->encryption_key; + *encryption_key_len = request->encryption_key_len; + return 1; + } + return 0; +} + +// int http_request_decrypt(http_request_t *request, char* decryption_key, ) { +// assert(request); + + +// } \ No newline at end of file diff --git a/lib/http_request.h b/lib/http_request.h index d72a6f11f..cadd90686 100644 --- a/lib/http_request.h +++ b/lib/http_request.h @@ -32,6 +32,8 @@ const char *http_request_get_protocol(http_request_t *request); const char *http_request_get_header(http_request_t *request, const char *name); const char *http_request_get_data(http_request_t *request, int *datalen); int http_request_get_header_string(http_request_t *request, char **header_str); +void http_request_begin_encryption(http_request_t *request, unsigned char *decryption_key, int decryption_key_len, unsigned char *encryption_key, int encryption_key_len); +int http_request_get_encryption(http_request_t *request, unsigned char **decryption_key, int *decryption_key_len, unsigned char **encryption_key, int *encryption_key_len); void http_request_destroy(http_request_t *request); diff --git a/lib/httpd.c b/lib/httpd.c index 83a288919..ba4e3b805 100644 --- a/lib/httpd.c +++ b/lib/httpd.c @@ -26,6 +26,7 @@ #include "http_request.h" #include "compat.h" #include "logger.h" +#include "srp.h" struct http_connection_s { int connected; @@ -34,6 +35,15 @@ struct http_connection_s { void *user_data; connection_type_t type; http_request_t *request; + + unsigned char *decryption_key; + int decryption_key_len; + + unsigned char *encryption_key; + int encryption_key_len; + + int decryption_counter; + int encryption_counter; }; typedef struct http_connection_s http_connection_t; @@ -347,6 +357,37 @@ httpd_thread(void *arg) continue; } + if (connection->decryption_key_len > 0) { + unsigned char * buf = buffer; + // uint16_t block_len = (uint16_t)((unsigned char)buffer[1] << 8 | (unsigned char)buffer[0]); + uint16_t block_len = ret - 18; + printf("%d", block_len); + int expected_payload_len = 2 + block_len + 16; + if (ret < expected_payload_len) { + logger_log(httpd->logger, LOGGER_INFO, "Incomplete payload, waiting for more data"); + continue; + } else if (ret > expected_payload_len) { + logger_log(httpd->logger, LOGGER_INFO, "Received multiple packets"); + } + int encrypted_len = block_len; + unsigned char nonce[NONCE_LENGTH] = { 0 }; + unsigned char tag[16]; + uint64_t counter = connection->decryption_counter; // re-write this so it's not assigning another local variable + memcpy(tag, buf + block_len + 2, 16); + unsigned char * result = malloc(encrypted_len); + uint16_t header; + memcpy(&header, buf, 2); + // nonce[4] = counter & 0xFF; + memcpy(nonce + 4, &counter, NONCE_LENGTH - 4); + + if (decrypt_chacha(result, buf + 2, block_len, connection->decryption_key, connection->decryption_key_len, buf, 2, tag, 16, nonce) != 0) { + logger_log(httpd->logger, LOGGER_ERR, "failed to decrypt packet"); + } + memcpy(buffer, result, encrypted_len); + ret = encrypted_len; + connection->decryption_counter++; + } + /* Parse HTTP request from data read from connection */ http_request_add_data(connection->request, buffer, ret); if (http_request_has_error(connection->request)) { @@ -367,6 +408,19 @@ httpd_thread(void *arg) "method = %s, url = %s, protocol = %s", connection->socket_fd, i, method, url, protocol); } httpd->callbacks.conn_request(connection->user_data, connection->request, &response); + unsigned char *decryption_key; + int decryption_key_len = 0; + unsigned char *encryption_key; + int encryption_key_len = 0; + if (http_request_get_encryption(connection->request, &decryption_key, &decryption_key_len, &encryption_key, &encryption_key_len)) { + connection->decryption_key = decryption_key; + connection->decryption_key_len = decryption_key_len; + connection->encryption_key = encryption_key; + connection->encryption_key_len = encryption_key_len; + connection->encryption_counter = 0; + connection->decryption_counter = 0; + } + http_request_destroy(connection->request); connection->request = NULL; @@ -379,6 +433,24 @@ httpd_thread(void *arg) /* Get response data and datalen */ data = http_response_get_data(response, &datalen); + if (connection->encryption_key_len > 0 && encryption_key_len == 0) { // .!decryption_key to make sure that the state did not change this frame (if it did then this frame would be accidentally encrypted) + unsigned char auth_tag[16]; + unsigned char nonce[NONCE_LENGTH]; + int total_packet_len = 2 + datalen + 16; + // uint16_t payload_len = htons(datalen); + unsigned char * encrypted = malloc(total_packet_len); // 2 bytes for payload size; datalen = payload; 16 bytes for authTag + memcpy(encrypted, &datalen, 2); + + uint64_t counter = connection->encryption_counter; + memcpy(nonce + 4, &counter, NONCE_LENGTH - 4); + + encrypt_chacha(encrypted + 2, data, datalen, connection->encryption_key, connection->encryption_key_len, encrypted, 2, auth_tag, 16, nonce); + memcpy(encrypted + 2 + datalen, auth_tag, 16); + data = encrypted; + datalen = total_packet_len; + connection->encryption_counter++; + } + written = 0; while (written < datalen) { ret = send(connection->socket_fd, data+written, datalen-written, 0); diff --git a/lib/pairing.c b/lib/pairing.c index e60273cd7..817728828 100644 --- a/lib/pairing.c +++ b/lib/pairing.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -308,13 +309,13 @@ random_pin() { int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin, const char **salt, int *len_salt, const char **pk, int *len_pk) { - if (strlen(device_id) > SRP_USERNAME_SIZE) { - return -1; - } + // if (strlen(device_id) > SRP_USERNAME_SIZE) { + // return -1; + // } - strncpy(session->username, device_id, SRP_USERNAME_SIZE); + strncpy(session->username, device_id, 10); - if (session->srp) { + if (session->srp) { free (session->srp); session->srp = NULL; } @@ -326,6 +327,9 @@ srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_ get_random_bytes(session->srp->private_key, SRP_PRIVATE_KEY_SIZE); const unsigned char *srp_b = session->srp->private_key; + // const unsigned char b_bytes[] = { 0x36, 0xBF, 0x27, 0xC5, 0xD3, 0x38, 0xCA, 0x0A, 0xCA, 0xB0, 0x4E, 0x09, 0xEB, 0x8F, 0x3F, 0x7A, 0x8E, 0xCB, 0x6C, 0xB5, 0x84, 0x01, 0x34, 0xCF, 0x71, 0x4D, 0x2F, 0xB2, 0x61, 0xCF, 0xFA, 0x31 }; + // const unsigned char *srp_b = b_bytes; + // memcpy(session->srp->private_key, b_bytes, 32); unsigned char * srp_B; unsigned char * srp_s; unsigned char * srp_v; @@ -354,6 +358,10 @@ srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_ (const unsigned char **) &srp_B, &len_B, NULL, NULL, 1); + // for (size_t i = 0; i < 384; i++) { + // printf("%02X ", srp_B[i]); + // } + *pk = (char *) srp_B; *len_pk = len_B; @@ -362,7 +370,7 @@ srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_ int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A, - int len_A, unsigned char *proof, int client_proof_len, int proof_len) { + int len_A, unsigned char *proof, int client_proof_len, int proof_len, unsigned char * decryption_key, int * decryption_key_len, unsigned char * encryption_key, int * encryption_key_len) { int authenticated = 0; const unsigned char *B = NULL; const unsigned char *b = session->srp->private_key; @@ -395,6 +403,11 @@ srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigne memcpy(session->srp->session_key, session_key, len_K); memcpy(proof, M2, proof_len); srp_verifier_delete(verifier); + + hkdf_get_key(verifier, decryption_key, decryption_key_len, PAIR_CONTROL_WRITE); + + hkdf_get_key(verifier, encryption_key, encryption_key_len, PAIR_CONTROL_READ); + return 0; } int diff --git a/lib/pairing.h b/lib/pairing.h index 54db62b17..24216345b 100644 --- a/lib/pairing.h +++ b/lib/pairing.h @@ -21,13 +21,14 @@ #define PAIRING_SIG_SIZE (2 * X25519_KEY_SIZE) -#define SRP_USERNAME_SIZE 24 /* accomodates up to an 8-octet MAC address */ -#define SRP_SESSION_KEY_SIZE 40 -#define SRP_VERIFIER_SIZE 256 +#define SRP_USERNAME "Pair-Setup" +#define SRP_USERNAME_SIZE 10 +#define SRP_SESSION_KEY_SIZE 64 +#define SRP_VERIFIER_SIZE 384 #define SRP_SALT_SIZE 16 -#define SRP_PK_SIZE 256 -#define SRP_SHA SRP_SHA1 -#define SRP_NG SRP_NG_2048 +#define SRP_PK_SIZE 384 +#define SRP_SHA SRP_SHA512 +#define SRP_NG SRP_NG_3072 #define SRP_M2_SIZE 64 #define SRP_PRIVATE_KEY_SIZE 32 #define GCM_AUTHTAG_SIZE 16 @@ -57,7 +58,7 @@ int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_s int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin, const char **salt, int *len_salt, const char **pk, int *len_pk); int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A, - int len_A, unsigned char *proof, int client_proof_len, int proof_len); + int len_A, unsigned char *proof, int client_proof_len, int proof_len, unsigned char * decryption_key, int * decryption_key_len, unsigned char * encryption_key, int * encryption_key_len); int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk, unsigned char *auth_tag); void access_client_session_data(pairing_session_t *session, char **username, char **client_pk, bool *setup); diff --git a/lib/raop.c b/lib/raop.c index 4c65736e3..eabe6e3bf 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -170,11 +170,8 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { const char *protocol = http_request_get_protocol(request); const char *cseq = http_request_get_header(request, "CSeq"); - printf("Test"); if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) { - printf("Test2"); if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) { - printf("Test3"); char ipaddr[40]; utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr))); if (httpd_nohold(conn->raop->httpd)) { @@ -214,17 +211,17 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { // if (!cseq) { // return; // } - // printf("Test4"); + logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol); char *header_str= NULL; http_request_get_header_string(request, &header_str); if (header_str) { logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", header_str); - bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); - bool data_is_text = (strstr(header_str,"text/") != NULL); free(header_str); int request_datalen; const char *request_data = http_request_get_data(request, &request_datalen); + bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL && strstr(request_data, "bplist") != NULL); /* Server sometimes says binary-plist is the content when it's not */ + bool data_is_text = (strstr(header_str,"text/") != NULL); if (request_data && logger_debug) { if (request_datalen > 0) { if (data_is_plist) { @@ -259,7 +256,6 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { raop_handler_t handler = NULL; if (!strcmp(method, "GET") && strstr(url, "/info") != NULL) { handler = &raop_handler_info; - logger_log(conn->raop->logger, LOGGER_DEBUG, "Correct path taken!!!!!"); } else if (!strcmp(method, "POST") && !strcmp(url, "/pair-pin-start")) { handler = &raop_handler_pairpinstart; } else if (!strcmp(method, "POST") && !strcmp(url, "/pair-setup-pin")) { diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index fee361912..c045e54aa 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -23,6 +23,7 @@ #include #include #include +#include "http_request.h" #define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */ #define SECOND_IN_USECS 1000000 @@ -110,6 +111,10 @@ raop_handler_info(raop_conn_t *conn, plist_t keep_alive_send_stats_as_body_node = plist_new_uint(1); plist_dict_set_item(res_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node); + plist_t features_ex_node = plist_new_string("1c9/St5PFbgmIQQ"); + plist_dict_set_item(res_node, "featuresEx", features_ex_node); + + plist_t audio_latencies_node = plist_new_array(); plist_t audio_latencies_0_node = plist_new_dict(); plist_t audio_latencies_0_output_latency_micros_node = plist_new_bool(0); @@ -147,12 +152,17 @@ raop_handler_info(raop_conn_t *conn, plist_t displays_0_width_node = plist_new_uint(conn->raop->width); plist_t displays_0_height_node = plist_new_uint(conn->raop->height); plist_t displays_0_width_pixels_node = plist_new_uint(conn->raop->width); + plist_t displays_0_max_width_pixels_node = plist_new_uint(3840); plist_t displays_0_height_pixels_node = plist_new_uint(conn->raop->height); + plist_t displays_0_max_height_pixels_node = plist_new_uint(2160); plist_t displays_0_rotation_node = plist_new_bool(0); plist_t displays_0_refresh_rate_node = plist_new_uint(conn->raop->refreshRate); plist_t displays_0_max_fps_node = plist_new_uint(conn->raop->maxFPS); plist_t displays_0_overscanned_node = plist_new_bool(conn->raop->overscanned); plist_t displays_0_features = plist_new_uint(14); + plist_t displays_0_supports444 = plist_new_bool(1); + // unsigned char edid[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0xAE, 0xFB, 0x65, 0x00, 0x00, 0x00, 0x00, 0x33, 0x1D, 0x01, 0x03, 0x80, 0x35, 0x1E, 0x78, 0x2E, 0xFF, 0xD5, 0xA9, 0x53, 0x45, 0xA0, 0x25, 0x0D, 0x50, 0x54, 0xBD, 0xEF, 0x00, 0x81, 0x80, 0x95, 0x00, 0xB3, 0x00, 0xD1, 0xC0, 0xD1, 0x00, 0x71, 0x4F, 0x81, 0x8A, 0x01, 0x01, 0x56, 0x5E, 0x00, 0xA0, 0xA0, 0xA0, 0x29, 0x50, 0x30, 0x20, 0x35, 0x00, 0x0F, 0x28, 0x21, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x30, 0x4B, 0x1E, 0x72, 0x1E, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4C, 0x45, 0x4E, 0x20, 0x4C, 0x32, 0x34, 0x71, 0x2D, 0x33, 0x30, 0x0A, 0x20, 0x01, 0x07, 0x02, 0x03, 0x27, 0xF1, 0x4B, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x13, 0x90, 0x1F, 0x12, 0x11, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0C, 0x00, 0x10, 0x00, 0x68, 0x1A, 0x00, 0x00, 0x01, 0x01, 0x30, 0x4B, 0x00, 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, 0x28, 0x55, 0x00, 0x0F, 0x28, 0x21, 0x00, 0x00, 0x1E, 0x8C, 0x0A, 0xD0, 0x8A, 0x20, 0xE0, 0x2D, 0x10, 0x10, 0x3E, 0x96, 0x00, 0x0F, 0x28, 0x21, 0x00, 0x00, 0x18, 0xCC, 0x74, 0x00, 0xA0, 0xA0, 0xA0, 0x1E, 0x50, 0x30, 0x20, 0x35, 0x00, 0x0F, 0x28, 0x21, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9 }; + // plist_t displays_0_edid = plist_new_data(edid, sizeof(edid)); plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node); plist_dict_set_item(displays_0_node, "widthPhysical", displays_0_width_physical_node); @@ -160,12 +170,16 @@ raop_handler_info(raop_conn_t *conn, plist_dict_set_item(displays_0_node, "width", displays_0_width_node); plist_dict_set_item(displays_0_node, "height", displays_0_height_node); plist_dict_set_item(displays_0_node, "widthPixels", displays_0_width_pixels_node); + plist_dict_set_item(displays_0_node, "widthPixelsMax", displays_0_max_width_pixels_node); plist_dict_set_item(displays_0_node, "heightPixels", displays_0_height_pixels_node); + plist_dict_set_item(displays_0_node, "heightPixelsMax", displays_0_max_height_pixels_node); plist_dict_set_item(displays_0_node, "rotation", displays_0_rotation_node); plist_dict_set_item(displays_0_node, "refreshRate", displays_0_refresh_rate_node); plist_dict_set_item(displays_0_node, "maxFPS", displays_0_max_fps_node); plist_dict_set_item(displays_0_node, "overscanned", displays_0_overscanned_node); plist_dict_set_item(displays_0_node, "features", displays_0_features); + plist_dict_set_item(displays_0_node, "receiverSupports444", displays_0_supports444); + // plist_dict_set_item(displays_0_edid, "edid", displays_0_features); plist_array_append_item(displays_node, displays_0_node); plist_dict_set_item(res_node, "displays", displays_node); @@ -296,7 +310,7 @@ raop_handler_pairsetup_pin(raop_conn_t *conn, memcpy(proof, client_proof, (int) client_proof_len); free (client_proof); int ret = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, - (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof)); + (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof), NULL, NULL, NULL, NULL); free (client_pk); plist_free(req_root_node); if (ret < 0) { @@ -368,24 +382,96 @@ raop_handler_pairsetup(raop_conn_t *conn, char **response_data, int *response_datalen) { unsigned char public_key[ED25519_KEY_SIZE]; - //const char *data; + const char *data; int datalen; - //data = + data = http_request_get_data(request, &datalen); - if (datalen != 32) { - logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data"); - return; - } + // if (datalen != 32) { + // logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data"); + // return; + // } + + if (datalen == 32) { + logger_log(conn->raop->logger, LOGGER_DEBUG, "PAIRING: Legacy pairing"); + pairing_get_public_key(conn->raop->pairing, public_key); + pairing_session_set_setup_status(conn->session); - pairing_get_public_key(conn->raop->pairing, public_key); - pairing_session_set_setup_status(conn->session); + *response_data = malloc(sizeof(public_key)); + if (*response_data) { + http_response_add_header(response, "Content-Type", "application/octet-stream"); + memcpy(*response_data, public_key, sizeof(public_key)); + *response_datalen = sizeof(public_key); + } + } else { + switch (data[2]) { + case 0x00: // M1 (usually 0x01, 0x06, 0x00, 0x01) + const char *salt; + // char pin[6]; + const char *pk; + int len_pk, len_salt; + char *method = NULL; + // char *user = NULL; + int ret = srp_new_user(conn->session, conn->raop->pairing, "Pair-Setup", + "3939", &salt, &len_salt, &pk, &len_pk); + *response_data = malloc(409); + char *res_data = *response_data; + *response_datalen = 409; + + res_data[0] = 0x06; + res_data[1] = 0x01; + res_data[2] = 0x02; + res_data[3] = 0x02; + res_data[4] = 0x10; + memcpy(&res_data[5], salt, len_salt); + res_data[21] = 0x03; + res_data[22] = 0xff; + memcpy(&res_data[23], pk, 255); + res_data[278] = 0x03; + res_data[279] = 0x81; + memcpy(&res_data[280], &pk[255], 0x81); + break; + case 0x03: // M3 + unsigned char *client_pk = malloc(384); + unsigned char proof[64]; + unsigned char *client_proof = malloc(64); + memcpy(client_pk, &data[5], 0xff); + memcpy(&client_pk[0xff], &data[262], 0x81); + memcpy(client_proof, &data[393], 0x40); + memcpy(proof, client_proof, 0x40); + + // unsigned char original_pk[] = { 0xa4, 0x1c, 0x3e, 0xec, 0x53, 0xbd, 0xd3, 0x99, 0xb5, 0x01, 0xc1, 0x1a, 0xda, 0x62, 0x04, 0x97, 0x43, 0x8e, 0xaf, 0x83, 0x8c, 0x4a, 0x3f, 0xc9, 0x96, 0xfb, 0x83, 0xae, 0x94, 0x0c, 0x94, 0x53, 0xc2, 0xac, 0x89, 0x82, 0x0b, 0xd4, 0x01, 0x38, 0x09, 0xae, 0x5e, 0x1a, 0xdf, 0xf2, 0x16, 0xf5, 0x06, 0xdc, 0x7b, 0x9e, 0x73, 0xc3, 0x90, 0x96, 0xb8, 0x67, 0xe7, 0x31, 0x95, 0x65, 0x3d, 0x4b, 0x17, 0x54, 0xbf, 0xcd, 0x08, 0xc6, 0x6f, 0x16, 0xec, 0x64, 0x05, 0xcf, 0x45, 0xae, 0x72, 0xbe, 0xef, 0xd4, 0x15, 0x0d, 0x86, 0x48, 0xc8, 0x11, 0xda, 0x1f, 0xde, 0xe0, 0xca, 0xda, 0x3a, 0x72, 0x44, 0x6d, 0xd5, 0x22, 0x40, 0x29, 0x00, 0x66, 0xbf, 0x16, 0x6a, 0x7f, 0x0c, 0x51, 0x37, 0x9d, 0xfd, 0xbd, 0xcd, 0x0c, 0x25, 0x1a, 0x21, 0xae, 0xd7, 0x1f, 0x07, 0xbf, 0xe4, 0xf4, 0x4d, 0x62, 0x69, 0x91, 0x7c, 0x6c, 0xa1, 0x8a, 0xcb, 0x4e, 0xc7, 0x0c, 0x6f, 0xf9, 0xbb, 0x1d, 0xeb, 0x07, 0x2f, 0xde, 0x0b, 0x15, 0xf4, 0xf7, 0xc1, 0xa7, 0xe2, 0x07, 0x54, 0x96, 0x7b, 0xea, 0x3e, 0x3f, 0x6c, 0xdc, 0xcc, 0x15, 0xf1, 0x5e, 0x94, 0x13, 0xc8, 0xca, 0x2f, 0xa5, 0xc9, 0xc0, 0x97, 0x34, 0xd5, 0xd4, 0x82, 0xac, 0xcb, 0x6b, 0x14, 0x00, 0x4a, 0x3a, 0x75, 0xb8, 0x6c, 0x73, 0x46, 0xdf, 0xb7, 0x04, 0x76, 0x1d, 0x52, 0xcc, 0x84, 0xe8, 0xda, 0x17, 0x49, 0xf1, 0x5b, 0x02, 0x43, 0xde, 0x7b, 0x28, 0xa7, 0x9d, 0xa8, 0xc0, 0xf3, 0x9f, 0x3e, 0x96, 0x41, 0x33, 0xa7, 0xb5, 0xe4, 0x40, 0x03, 0xe9, 0x53, 0x05, 0x05, 0x5c, 0x6c, 0xd4, 0x3c, 0xb1, 0xcc, 0x07, 0x0b, 0xeb, 0xbc, 0xce, 0x78, 0x45, 0x73, 0x68, 0xed, 0xba, 0xa1, 0x77, 0x17, 0x7a, 0x0e, 0xf0, 0xb8, 0x3e, 0x10, 0x97, 0xce, 0x2e, 0x31, 0x91, 0x4b, 0xcf, 0x40, 0xf7, 0x6d, 0x87, 0xa6, 0x3f, 0x72, 0xc1, 0xbb, 0x90, 0x58, 0x51, 0x4d, 0x6b, 0x1a, 0x64, 0x46, 0xc7, 0xc1, 0xc6, 0x11, 0xf4, 0xf5, 0xff, 0xe5, 0xcf, 0xae, 0xd9, 0x74, 0xaf, 0x65, 0x41, 0xa0, 0x80, 0xc5, 0xc0, 0x36, 0x58, 0x41, 0x94, 0xb7, 0xd9, 0xe0, 0x52, 0x35, 0xb3, 0xe4, 0x0a, 0xd8, 0x35, 0xfd, 0x49, 0xbf, 0x7c, 0x33, 0x9b, 0x40, 0x2d, 0x02, 0x4b, 0xd9, 0x6e, 0xc0, 0x96, 0x01, 0x8b, 0xd6, 0x5f, 0x34, 0xce, 0xd5, 0xc5, 0x90, 0x68, 0x11, 0x67, 0x60, 0xa4, 0x99, 0x48, 0x9c, 0x2c, 0xb2, 0x64, 0x25, 0x50, 0x25, 0x5e, 0xe7, 0x56, 0x6d, 0x9a, 0x6c, 0x1a, 0x5b, 0x15, 0x4c, 0xe2, 0xed, 0x4f, 0x6a, 0xb9, 0x31, 0x18, 0x6e, 0x91, 0xb3, 0x8a, 0xed, 0xab, 0x31, 0x40, 0x0f, 0xae, 0xbd, 0x58, 0x0b, 0x79, 0xed, 0xc5, 0x8d, 0x14 }; + // client_pk = original_pk; + + // unsigned char original_proof[] = { 0x7a, 0x00, 0xef, 0x8b, 0xd8, 0x4d, 0xb0, 0x99, 0x4a, 0x74, 0x94, 0x22, 0x49, 0x66, 0x03, 0xfb, 0xf9, 0x66, 0x63, 0x68, 0x83, 0x78, 0x05, 0x6e, 0xcf, 0x4a, 0xab, 0x2c, 0x1f, 0x82, 0x9a, 0xe4, 0xc3, 0xb3, 0x0d, 0x7f, 0xc6, 0x30, 0xb3, 0x29, 0x69, 0xd4, 0xec, 0xc8, 0xc2, 0x7c, 0x17, 0x47, 0x82, 0xe1, 0x54, 0x15, 0x91, 0x7d, 0x1f, 0xbe, 0xca, 0xd3, 0xaf, 0xfd, 0xda, 0x5f, 0x6b, 0x8f }; + // memcpy(proof, original_proof, 0x40); + + unsigned char * decryption_key = malloc(32); + int decryption_key_len = 32; + unsigned char * encryption_key = malloc(32); + int encryption_key_len = 32; + + int ret2 = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, + 384, proof, 0x40, 0x40, decryption_key, &decryption_key_len, encryption_key, &encryption_key_len); + if (ret2 < 0) { + logger_log(conn->raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated) %d", ret2); + } + + *response_data = malloc(69); + char *res_data2 = *response_data; + *response_datalen = 69; - *response_data = malloc(sizeof(public_key)); - if (*response_data) { - http_response_add_header(response, "Content-Type", "application/octet-stream"); - memcpy(*response_data, public_key, sizeof(public_key)); - *response_datalen = sizeof(public_key); + res_data2[0] = 0x06; + res_data2[1] = 0x01; + res_data2[2] = 0x04; + res_data2[3] = 0x04; + res_data2[4] = 0x40; + memcpy(&res_data2[5], proof, 0x40); + + http_request_begin_encryption(request, decryption_key, decryption_key_len, encryption_key, encryption_key_len); + + break; + } } } diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c index 141c65eb4..62b53bbc3 100644 --- a/lib/raop_rtp_mirror.c +++ b/lib/raop_rtp_mirror.c @@ -383,7 +383,7 @@ raop_rtp_mirror_thread(void *arg) } unsigned char* payload_out; - unsigned char* payload_decrypted; + unsigned char* payload_decrypted; /* * nal_types:1 Coded non-partitioned slice of a non-IDR picture * 5 Coded non-partitioned slice of an IDR picture @@ -415,7 +415,7 @@ raop_rtp_mirror_thread(void *arg) payload_decrypted = payload_out + sps_pps_len; memcpy(payload_out, sps_pps, sps_pps_len); free (sps_pps); - sps_pps = NULL; + sps_pps = NULL; } else { payload_out = (unsigned char*) malloc(payload_size); payload_decrypted = payload_out; @@ -464,7 +464,7 @@ raop_rtp_mirror_thread(void *arg) case 5: /*IDR, slice_layer_without_partitioning */ case 1: /*non-IDR, slice_layer_without_partitioning */ break; - case 2: /* slice data partition A */ + case 2: /* slice data partition A */ case 3: /* slice data partition B */ case 4: /* slice data partition C */ logger_log(raop_rtp_mirror->logger, LOGGER_INFO, diff --git a/lib/srp.c b/lib/srp.c index 6190de7e8..308eb0bdd 100644 --- a/lib/srp.c +++ b/lib/srp.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -93,7 +94,6 @@ static struct NGHex global_Ng_constants[] = { "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", "2" }, -#if 0 /* begin removed section 2 */ { /* 3072 */ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B" "139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485" @@ -108,6 +108,7 @@ static struct NGHex global_Ng_constants[] = { "D120A93AD2CAFFFFFFFFFFFFFFFF", "5" }, +#if 0 /* begin removed section 2 */ { /* 4096 */ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" @@ -205,6 +206,40 @@ static struct NGHex global_Ng_constants[] = { {0,0} /* null sentinel */ }; +struct pair_keys_map +{ + uint8_t state; + const char *salt; + const char *info; + const char nonce[8]; +}; + +static struct pair_keys_map pair_keys_map[] = +{ + // Used for /pair-setup + { 0x01, NULL, NULL, "" }, + { 0x02, NULL, NULL, "" }, + { 0x03, NULL, NULL, "" }, + { 0x04, NULL, NULL, "" }, + { 0x05, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg05" }, + { 0x06, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg06" }, + { 0, "Pair-Setup-Controller-Sign-Salt", "Pair-Setup-Controller-Sign-Info", "" }, + { 0, "Pair-Setup-Accessory-Sign-Salt", "Pair-Setup-Accessory-Sign-Info", "" }, + + // Used for /pair-verify + { 0x01, NULL, NULL, "" }, + { 0x02, "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", "PV-Msg02" }, + { 0x03, "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", "PV-Msg03" }, + { 0x04, NULL, NULL, "" }, + + // Encryption/decryption of control channel + { 0, "Control-Salt", "Control-Write-Encryption-Key", "" }, + { 0, "Control-Salt", "Control-Read-Encryption-Key", "" }, + + // Encryption/decryption of event channel + { 0, "Events-Salt", "Events-Write-Encryption-Key", "" }, + { 0, "Events-Salt", "Events-Read-Encryption-Key", "" }, +}; static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex ) { @@ -295,7 +330,7 @@ static void handle_error(const char* location) { long error = ERR_get_error(); const char* error_str = ERR_error_string(error, NULL); fprintf(stderr, "SRP error at %s: %s\n", location, error_str); - exit(EXIT_FAILURE); + // exit(EXIT_FAILURE); } static void hash_destroy( HashCTX_t *ctx) { @@ -543,11 +578,11 @@ static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * update_hash_n( alg, ctx, s ); update_hash_n( alg, ctx, A ); update_hash_n( alg, ctx, B ); -#ifdef APPLE_VARIANT /* Apple's SRP session key length is 2 x hash_len */ - hash_update( alg, ctx, K, 2 * hash_len ); -#else +// #ifdef APPLE_VARIANT /* Apple's SRP session key length is 2 x hash_len */ +// hash_update( alg, ctx, K, 2 * hash_len ); +// #else hash_update( alg, ctx, K, hash_len ); -#endif +// #endif hash_final( alg, ctx, dest, &dest_len ); hash_destroy ( ctx); @@ -562,11 +597,11 @@ static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const B hash_init( alg, ctx); update_hash_n( alg, ctx, A ); hash_update( alg, ctx, M, hash_length(alg) ); -#ifdef APPLE_VARIANT - hash_update( alg, ctx, K, 2 * hash_length(alg) ); -#else +// #ifdef APPLE_VARIANT +// hash_update( alg, ctx, K, 2 * hash_length(alg) ); +// #else hash_update( alg, ctx, K, hash_length(alg) ); -#endif +// #endif hash_final( alg, ctx, dest, &dest_len ); hash_destroy ( ctx); } @@ -639,8 +674,10 @@ void srp_create_salted_verification_key( SRP_HashAlgorithm alg, #ifdef APPLE_VARIANT //use a 16 byte salt BN_rand(s, 128, -1, 0); + // unsigned char salt[] = { 0x10, 0x24, 0x36, 0x7E, 0x07, 0xF4, 0xB4, 0xBC, 0xB1, 0x4A, 0x17, 0x6D, 0x80, 0xA5, 0xC7, 0x59 }; + // s = BN_bin2bn(salt, 16, NULL); #else - BN_rand(s, 32, -1, 0); + // BN_rand(s, 32, -1, 0); #endif x = calculate_x( alg, s, username, password, len_password ); @@ -845,21 +882,34 @@ struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_typ goto cleanup_and_exit; } - /* S = (A *(v^u)) ^ b */ - BN_mod_exp(tmp1, v, u, ng->N, ctx); - BN_mul(tmp2, A, tmp1, ctx); - BN_mod_exp(S, tmp2, b, ng->N, ctx); + /* S = (A *(v^u)) ^ b */ + BN_mod_exp(tmp1, v, u, ng->N, ctx); + BN_mul(tmp2, A, tmp1, ctx); + BN_mod_exp(S, tmp2, b, ng->N, ctx); -#ifdef APPLE_VARIANT - hash_session_key(alg, S, ver->session_key); -#else - hash_num(alg, S, ver->session_key); -#endif - calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); - calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); + const unsigned char *bytes_S; + int len_S = BN_num_bytes(S); + bytes_S = (const unsigned char *)malloc( len_S ); + BN_bn2bin(S, (unsigned char *) bytes_S); + + + +// #ifdef APPLE_VARIANT +// hash_session_key(alg, S, ver->session_key); +// #else + hash_num(alg, S, ver->session_key); +// #endif + calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); + calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); + + // printf("M (%d): ", 64); + // for (int i = 0; i < 64; i++) { + // printf("%02X ", ver->M[i]); + // } + // printf("\n\n\n"); - *len_B = BN_num_bytes(B); - *bytes_B = (const unsigned char *)malloc( *len_B ); + *len_B = BN_num_bytes(B); + *bytes_B = (const unsigned char *)malloc( *len_B ); if( !((const unsigned char *)*bytes_B) ) { @@ -919,21 +969,21 @@ const char * srp_verifier_get_username( struct SRPVerifier * ver ) const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ) { if (key_length) -#ifdef APPLE_VARIANT - *key_length = 2 * hash_length( ver->hash_alg ); -#else +// #ifdef APPLE_VARIANT +// *key_length = 2 * hash_length( ver->hash_alg ); +// #else *key_length = hash_length( ver->hash_alg ); -#endif +// #endif return ver->session_key; } int srp_verifier_get_session_key_length( struct SRPVerifier * ver ) { -#ifdef APPLE_VARIANT - return 2 * hash_length( ver->hash_alg ); -#else +// #ifdef APPLE_VARIANT +// return 2 * hash_length( ver->hash_alg ); +// #else return hash_length( ver->hash_alg ); -#endif +// #endif } /* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */ @@ -948,6 +998,187 @@ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char *bytes_HAMK = NULL; } +/* Executes SHA512 RFC 5869 extract + expand, writing a derived key to okm + + hkdfExtract(SHA512, salt, salt_len, ikm, ikm_len, prk); + hkdfExpand(SHA512, prk, SHA512_LEN, info, info_len, okm, okm_len); +*/ +static int +hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, enum pair_keys pair_key) +{ +// #ifdef CONFIG_OPENSSL +#include + EVP_PKEY_CTX *pctx; + + if (okm_len > SHA512_DIGEST_LENGTH) + return -1; + if (! (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL))) + return -1; + if (EVP_PKEY_derive_init(pctx) <= 0) + goto error; + if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) <= 0) + goto error; + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)pair_keys_map[pair_key].salt, strlen(pair_keys_map[pair_key].salt)) <= 0) + goto error; + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len) <= 0) + goto error; + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)pair_keys_map[pair_key].info, strlen(pair_keys_map[pair_key].info)) <= 0) + goto error; + if (EVP_PKEY_derive(pctx, okm, &okm_len) <= 0) + goto error; + + EVP_PKEY_CTX_free(pctx); + return 0; + + error: + EVP_PKEY_CTX_free(pctx); + return -1; +// #else +// return -1; +// #endif +} + +int hkdf_get_key(struct SRPVerifier * ver, unsigned char * derived_key, int * derived_key_len, enum pair_keys msg_state) { + int session_key_len; + int ret; + + const unsigned char * session_key = srp_verifier_get_session_key(ver, &session_key_len); + if (!session_key) { + printf("Could not get session key");// + return -1; + } + ret = hkdf_extract_expand(derived_key, *derived_key_len, session_key, session_key_len, msg_state); + if (ret < 0) { + printf("%d", ret); + printf("hkdf derived key error"); + return -2; + } + + printf("%02X ", *derived_key); + printf("%02X ", derived_key[1]); + printf("%02X ", derived_key[2]); + printf("%02X ", derived_key[3]); + + return 0; +} + +int +decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]) +{ + EVP_CIPHER_CTX *ctx; + int len; + + const unsigned char * aad = ad; + + printf("DECRYPTION INFO:"); + + printf("\n\nCipher: (%d)\n", cipher_len); + for (int i = 0; i < cipher_len; i++) { + printf("%02X ", cipher[i]); + } + + printf("\n\nDecryption key (%d)\n", key_len); + for (int i = 0; i < key_len; i++) { + printf("%02X ", key[i]); + } + + printf("\n\nAD (%d)\n", ad_len); + for (int i = 0; i < ad_len; i++) { + printf("%02X ", aad[i]); + } + + printf("\n\nTag (%d)\n", tag_len); + for (int i = 0; i < tag_len; i++) { + printf("%02X ", tag[i]); + } + + printf("\n\nNonce\n"); + for (int i = 0; i < NONCE_LENGTH; i++) { + printf("%02X ", nonce[i]); + } + + + + if (! (ctx = EVP_CIPHER_CTX_new())) { + printf("here 1"); + return -1; + } + + if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce) != 1) { + printf("here 2"); + goto error; + } + + if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary + goto error; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1) + goto error; + + if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) { + printf("here 5"); + goto error; + } + + if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1) { + printf("here 6"); + goto error; + } + + if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1) { + printf("here 7: (%d)", len); + goto error; + } + + EVP_CIPHER_CTX_free(ctx); + return 0; + + error: + EVP_CIPHER_CTX_free(ctx); + return -1; +} + +int +encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]) +{ + EVP_CIPHER_CTX *ctx; + int len; + + if (! (ctx = EVP_CIPHER_CTX_new())) + return -1; + + if (EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce) != 1) + goto error; + + if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary + goto error; + + if (ad_len > 0 && EVP_EncryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) + goto error; + + if (EVP_EncryptUpdate(ctx, cipher, &len, plain, plain_len) != 1) + goto error; + + assert(len == plain_len); + + if (EVP_EncryptFinal_ex(ctx, NULL, &len) != 1) + goto error; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, tag) != 1) + goto error; + + EVP_CIPHER_CTX_free(ctx); + return 0; + + error: + EVP_CIPHER_CTX_free(ctx); + return -1; +} + +void get_error() { + handle_error("Here"); +} + /*******************************************************************************/ #if 0 /*begin removed section 4 */ struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, diff --git a/lib/srp.h b/lib/srp.h index 6af96a719..863b20b75 100644 --- a/lib/srp.h +++ b/lib/srp.h @@ -61,6 +61,10 @@ #define SRP_H #define APPLE_VARIANT +#include + +#define NONCE_LENGTH 12 // 96 bits according to chacha poly1305 + struct SRPVerifier; #if 0 /*begin removed section 1*/ struct SRPUser; @@ -72,8 +76,8 @@ typedef enum SRP_NG_1536, #endif /* end removed section 2*/ SRP_NG_2048, -#if 0 /* begin removed section 3*/ SRP_NG_3072, +#if 0 /* begin removed section 3*/ SRP_NG_4096, SRP_NG_6144, SRP_NG_8192, @@ -190,6 +194,34 @@ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK ); + +enum pair_keys +{ + PAIR_SETUP_MSG01 = 0, + PAIR_SETUP_MSG02, + PAIR_SETUP_MSG03, + PAIR_SETUP_MSG04, + PAIR_SETUP_MSG05, + PAIR_SETUP_MSG06, + PAIR_SETUP_CONTROLLER_SIGN, + PAIR_SETUP_ACCESSORY_SIGN, + PAIR_VERIFY_MSG01, + PAIR_VERIFY_MSG02, + PAIR_VERIFY_MSG03, + PAIR_VERIFY_MSG04, + PAIR_CONTROL_WRITE, + PAIR_CONTROL_READ, + PAIR_EVENTS_WRITE, + PAIR_EVENTS_READ, +}; + +int hkdf_get_key(struct SRPVerifier * ver, unsigned char * derived_key, int * derived_key_len, enum pair_keys msg_state); + +int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]); + +int encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]); + +void get_error(); /*******************************************************************************/ /* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type diff --git a/uxplay.cpp b/uxplay.cpp index f4c7d1e45..3ec9d26d4 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -1375,11 +1375,11 @@ static int start_dnssd(std::vector hw_addr, std::string name) { dnssd_set_airplay_features(dnssd, 30, 1); // RAOP support: with this bit set, the AirTunes service is not required. dnssd_set_airplay_features(dnssd, 31, 0); // - for (int i = 32; i < 64; i++) { - dnssd_set_airplay_features(dnssd, i, 0); - } + // for (int i = 32; i < 64; i++) { + // dnssd_set_airplay_features(dnssd, i, 0); + // } - /* bits 32-63 are not used here: see https://emanualcozzi.net/docs/airplay2/features + /* bits 32-63 are not used here: see https://emanualcozzi.net/docs/airplay2/features */ dnssd_set_airplay_features(dnssd, 32, 0); // isCarPlay when ON,; Supports InitialVolume when OFF dnssd_set_airplay_features(dnssd, 33, 0); // Supports Air Play Video Play Queue dnssd_set_airplay_features(dnssd, 34, 0); // Supports Air Play from cloud (requires that bit 6 is ON) @@ -1400,7 +1400,7 @@ static int start_dnssd(std::vector hw_addr, std::string name) { dnssd_set_airplay_features(dnssd, 46, 0); // Supports HomeKit Pairing and Access Control dnssd_set_airplay_features(dnssd, 47, 0); // - dnssd_set_airplay_features(dnssd, 48, 0); // Supports CoreUtils Pairing and Encryption + dnssd_set_airplay_features(dnssd, 48, 1); // Supports CoreUtils Pairing and Encryption dnssd_set_airplay_features(dnssd, 49, 0); // dnssd_set_airplay_features(dnssd, 50, 0); // Metadata bit 3: "Now Playing" info sent by bplist not DAACP test dnssd_set_airplay_features(dnssd, 51, 0); // Supports Unified Pair Setup and MFi Authentication @@ -1417,7 +1417,7 @@ static int start_dnssd(std::vector hw_addr, std::string name) { dnssd_set_airplay_features(dnssd, 60, 0); // Supports Audo Media Data Control dnssd_set_airplay_features(dnssd, 61, 0); // Supports RFC2198 redundancy - */ + /* bit 27 of Features determines whether the AirPlay2 client-pairing protocol will be used (1) or not (0) */ dnssd_set_airplay_features(dnssd, 27, (int) setup_legacy_pairing); From c53767882d07ef22a74f86435432043c7cb8a6b9 Mon Sep 17 00:00:00 2001 From: Connor Date: Sun, 1 Sep 2024 16:18:41 +0100 Subject: [PATCH 3/4] Moved + refactored a lot of the code + working video decryption --- lib/crypto.c | 187 +++++++++++++++++++++++ lib/crypto.h | 23 +++ lib/http_request.c | 24 +-- lib/http_request.h | 5 +- lib/httpd.c | 47 ++---- lib/mirror_buffer.c | 74 ++++++--- lib/mirror_buffer.h | 3 +- lib/pairing.c | 35 ++++- lib/pairing.h | 3 +- lib/raop_handlers.h | 135 +++++++++-------- lib/raop_rtp_mirror.c | 11 +- lib/raop_rtp_mirror.h | 1 + lib/srp.c | 219 +++++++++++---------------- lib/srp.h | 8 +- renderers/video_renderer_gstreamer.c | 7 + 15 files changed, 505 insertions(+), 277 deletions(-) diff --git a/lib/crypto.c b/lib/crypto.c index 906a2fe0b..d0a07ff26 100644 --- a/lib/crypto.c +++ b/lib/crypto.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -580,3 +581,189 @@ void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len) memcpy(pk_base64,(*bufferPtr).data, len64); } +// CHACHA + +/* Executes SHA512 RFC 5869 extract + expand, writing a derived key to okm + + hkdfExtract(SHA512, salt, salt_len, ikm, ikm_len, prk); + hkdfExpand(SHA512, prk, SHA512_LEN, info, info_len, okm, okm_len); +*/ +static int +hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, char * salt, char * info) +{ + EVP_PKEY_CTX *pctx; + + if (okm_len > SHA512_DIGEST_LENGTH) + return -1; + if (! (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL))) + return -1; + if (EVP_PKEY_derive_init(pctx) <= 0) + goto error; + if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) <= 0) + goto error; + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)salt, strlen(salt)) <= 0) + goto error; + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len) <= 0) + goto error; + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)info, strlen(info)) <= 0) + goto error; + if (EVP_PKEY_derive(pctx, okm, &okm_len) <= 0) + goto error; + + EVP_PKEY_CTX_free(pctx); + return 0; + + error: + EVP_PKEY_CTX_free(pctx); + return -1; +} + +int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info) { + return hkdf_extract_expand(derived_key, *derived_key_len, session_key, session_key_len, salt, info); +} + +/* Creates the decryption key or the encryption key (or both) for HomeKit-based connections + chacha_setup_keys(ctx, "DataStream-Salt123123123", "DataStream-Output-Encryption-Key", NULL, NULL); + chacha_setup_keys(ctx, "Events-Salt", "Events-Write-Encryption-Key", "Events-Salt", "Events-Read-Encryption-Key") + + Returns: + 0 on success + -1 on decryption key fail + -2 on encryption key fail +*/ +int +chacha_setup_keys(chacha_ctx_t *ctx, + unsigned char *session_key, + unsigned char *decryption_salt, unsigned char *decryption_info, + unsigned char *encryption_salt, unsigned char *encryption_info) +{ + assert(ctx); + + ctx->decryption_counter = 0; + ctx->encryption_counter = 0; + + if (decryption_salt != NULL) { + if (hkdf_extract_expand(ctx->decryption_key, CHACHA_KEY_LEN, session_key, HOMEKIT_SESSION_KEY_LEN, decryption_salt, decryption_info)) { + return -1; + } + } + + if (encryption_salt != NULL) { + if (hkdf_extract_expand(ctx->encryption_key, CHACHA_KEY_LEN, session_key, HOMEKIT_SESSION_KEY_LEN, encryption_salt, encryption_info)) { + return -2; + } + } + + return 0; +} + +#define CHACHA_DEBUG 0 + +int +decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, chacha_ctx_t *chacha_ctx, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len) +{ + EVP_CIPHER_CTX *ctx; + int len; + uint8_t nonce[12] = {0}; + memcpy(nonce + 4, &chacha_ctx->decryption_counter, 8); + chacha_ctx->decryption_counter++; + +#if CHACHA_DEBUG + const unsigned char * aad = ad; + + printf("DECRYPTION INFO:"); + + printf("\n\nCipher: (%d)\n", cipher_len); + for (int i = 0; i < cipher_len; i++) { + printf("%02X ", cipher[i]); + } + + printf("\n\nDecryption key (%d)\n", 32); + for (int i = 0; i < 32; i++) { + printf("%02X ", chacha_ctx->decryption_key[i]); + } + + printf("\n\nAD (%d)\n", ad_len); + for (int i = 0; i < ad_len; i++) { + printf("%02X ", aad[i]); + } + + printf("\n\nTag (%d)\n", tag_len); + for (int i = 0; i < tag_len; i++) { + printf("%02X ", tag[i]); + } + + printf("\n\nNonce\n"); + for (int i = 0; i < NONCE_LENGTH; i++) { + printf("%02X ", nonce[i]); + } +#endif + + if (! (ctx = EVP_CIPHER_CTX_new())) + return -1; + + if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, chacha_ctx->decryption_key, nonce) != 1) + goto error; + + if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary + goto error; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1) + goto error; + + if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) + goto error; + + if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1) + goto error; + + if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1) + goto error; + + EVP_CIPHER_CTX_free(ctx); + return 0; + + error: + EVP_CIPHER_CTX_free(ctx); + return -1; +} + +int +encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, chacha_ctx_t *chacha_ctx, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len) +{ + EVP_CIPHER_CTX *ctx; + int len; + uint8_t nonce[12]; + memcpy(nonce + 4, &chacha_ctx->encryption_counter, 8); + chacha_ctx->encryption_counter++; + + if (! (ctx = EVP_CIPHER_CTX_new())) + return -1; + + if (EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, chacha_ctx->encryption_key, nonce) != 1) + goto error; + + if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary + goto error; + + if (ad_len > 0 && EVP_EncryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) + goto error; + + if (EVP_EncryptUpdate(ctx, cipher, &len, plain, plain_len) != 1) + goto error; + + assert(len == plain_len); + + if (EVP_EncryptFinal_ex(ctx, NULL, &len) != 1) + goto error; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, tag) != 1) + goto error; + + EVP_CIPHER_CTX_free(ctx); + return 0; + + error: + EVP_CIPHER_CTX_free(ctx); + return -1; +} \ No newline at end of file diff --git a/lib/crypto.h b/lib/crypto.h index 7bd2d5e6f..7bfc9732f 100644 --- a/lib/crypto.h +++ b/lib/crypto.h @@ -109,6 +109,29 @@ void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len); void sha_reset(sha_ctx_t *ctx); void sha_destroy(sha_ctx_t *ctx); +// CHACHA + +#define NONCE_LENGTH 12 +#define CHACHA_KEY_LEN 32 +#define HOMEKIT_SESSION_KEY_LEN 64 + +struct chacha_ctx_s { + unsigned char decryption_key[CHACHA_KEY_LEN]; + uint64_t decryption_counter; + unsigned char encryption_key[CHACHA_KEY_LEN]; + uint64_t encryption_counter; +}; + +typedef struct chacha_ctx_s chacha_ctx_t; + +int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info); + +int chacha_setup_keys(chacha_ctx_t *ctx, unsigned char *session_key, unsigned char *decryption_salt, unsigned char *decryption_info, unsigned char *encryption_salt, unsigned char *encryption_info); + +int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, chacha_ctx_t *chacha_ctx, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len); + +int encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, chacha_ctx_t *chacha_ctx, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len); + #ifdef __cplusplus } #endif diff --git a/lib/http_request.c b/lib/http_request.c index 855aa92ad..3ca8242b7 100644 --- a/lib/http_request.c +++ b/lib/http_request.c @@ -22,6 +22,7 @@ #include "http_request.h" #include "llhttp/llhttp.h" +#include "crypto.h" struct http_request_s { llhttp_t parser; @@ -40,11 +41,7 @@ struct http_request_s { int complete; - unsigned char *decryption_key; - int decryption_key_len; - - unsigned char *encryption_key; - int encryption_key_len; + chacha_ctx_t *chacha_ctx; }; static int @@ -317,26 +314,21 @@ http_request_get_header_string(http_request_t *request, char **header_str) } void -http_request_begin_encryption(http_request_t *request, unsigned char * decryption_key, int decryption_key_len, unsigned char * encryption_key, int encryption_key_len) { +http_request_begin_encryption(http_request_t *request, chacha_ctx_t *chacha_ctx) { assert(request); - request->decryption_key = decryption_key; - request->decryption_key_len = decryption_key_len; - request->encryption_key = encryption_key; - request->encryption_key_len = encryption_key_len; + request->chacha_ctx = chacha_ctx; } int -http_request_get_encryption(http_request_t *request, unsigned char ** decryption_key, int *decryption_key_len, unsigned char ** encryption_key, int *encryption_key_len) { +http_request_get_encryption(http_request_t *request, chacha_ctx_t **chacha_ctx) { assert(request); - if (request->decryption_key_len > 0) { - *decryption_key = request->decryption_key; - *decryption_key_len = request->decryption_key_len; - *encryption_key = request->encryption_key; - *encryption_key_len = request->encryption_key_len; + if (request->chacha_ctx) { + *chacha_ctx = request->chacha_ctx; return 1; } + return 0; } diff --git a/lib/http_request.h b/lib/http_request.h index cadd90686..36bc32a99 100644 --- a/lib/http_request.h +++ b/lib/http_request.h @@ -17,6 +17,7 @@ typedef struct http_request_s http_request_t; +typedef struct chacha_ctx_s chacha_ctx_t; http_request_t *http_request_init(void); @@ -32,8 +33,8 @@ const char *http_request_get_protocol(http_request_t *request); const char *http_request_get_header(http_request_t *request, const char *name); const char *http_request_get_data(http_request_t *request, int *datalen); int http_request_get_header_string(http_request_t *request, char **header_str); -void http_request_begin_encryption(http_request_t *request, unsigned char *decryption_key, int decryption_key_len, unsigned char *encryption_key, int encryption_key_len); -int http_request_get_encryption(http_request_t *request, unsigned char **decryption_key, int *decryption_key_len, unsigned char **encryption_key, int *encryption_key_len); +void http_request_begin_encryption(http_request_t *request, chacha_ctx_t *ctx); +int http_request_get_encryption(http_request_t *request, chacha_ctx_t **ctx); void http_request_destroy(http_request_t *request); diff --git a/lib/httpd.c b/lib/httpd.c index ba4e3b805..40ddc8c46 100644 --- a/lib/httpd.c +++ b/lib/httpd.c @@ -26,7 +26,7 @@ #include "http_request.h" #include "compat.h" #include "logger.h" -#include "srp.h" +#include "crypto.h" struct http_connection_s { int connected; @@ -36,14 +36,7 @@ struct http_connection_s { connection_type_t type; http_request_t *request; - unsigned char *decryption_key; - int decryption_key_len; - - unsigned char *encryption_key; - int encryption_key_len; - - int decryption_counter; - int encryption_counter; + chacha_ctx_t *chacha_ctx; }; typedef struct http_connection_s http_connection_t; @@ -357,11 +350,9 @@ httpd_thread(void *arg) continue; } - if (connection->decryption_key_len > 0) { + if (connection->chacha_ctx) { unsigned char * buf = buffer; - // uint16_t block_len = (uint16_t)((unsigned char)buffer[1] << 8 | (unsigned char)buffer[0]); uint16_t block_len = ret - 18; - printf("%d", block_len); int expected_payload_len = 2 + block_len + 16; if (ret < expected_payload_len) { logger_log(httpd->logger, LOGGER_INFO, "Incomplete payload, waiting for more data"); @@ -370,22 +361,17 @@ httpd_thread(void *arg) logger_log(httpd->logger, LOGGER_INFO, "Received multiple packets"); } int encrypted_len = block_len; - unsigned char nonce[NONCE_LENGTH] = { 0 }; unsigned char tag[16]; - uint64_t counter = connection->decryption_counter; // re-write this so it's not assigning another local variable memcpy(tag, buf + block_len + 2, 16); unsigned char * result = malloc(encrypted_len); uint16_t header; memcpy(&header, buf, 2); - // nonce[4] = counter & 0xFF; - memcpy(nonce + 4, &counter, NONCE_LENGTH - 4); - if (decrypt_chacha(result, buf + 2, block_len, connection->decryption_key, connection->decryption_key_len, buf, 2, tag, 16, nonce) != 0) { + if (decrypt_chacha(result, buf + 2, block_len, connection->chacha_ctx, buf, 2, tag, 16) != 0) { logger_log(httpd->logger, LOGGER_ERR, "failed to decrypt packet"); } memcpy(buffer, result, encrypted_len); ret = encrypted_len; - connection->decryption_counter++; } /* Parse HTTP request from data read from connection */ @@ -408,17 +394,10 @@ httpd_thread(void *arg) "method = %s, url = %s, protocol = %s", connection->socket_fd, i, method, url, protocol); } httpd->callbacks.conn_request(connection->user_data, connection->request, &response); - unsigned char *decryption_key; - int decryption_key_len = 0; - unsigned char *encryption_key; - int encryption_key_len = 0; - if (http_request_get_encryption(connection->request, &decryption_key, &decryption_key_len, &encryption_key, &encryption_key_len)) { - connection->decryption_key = decryption_key; - connection->decryption_key_len = decryption_key_len; - connection->encryption_key = encryption_key; - connection->encryption_key_len = encryption_key_len; - connection->encryption_counter = 0; - connection->decryption_counter = 0; + + chacha_ctx_t *chacha_ctx = NULL; + if (http_request_get_encryption(connection->request, &chacha_ctx)) { + connection->chacha_ctx = chacha_ctx; } http_request_destroy(connection->request); @@ -433,22 +412,16 @@ httpd_thread(void *arg) /* Get response data and datalen */ data = http_response_get_data(response, &datalen); - if (connection->encryption_key_len > 0 && encryption_key_len == 0) { // .!decryption_key to make sure that the state did not change this frame (if it did then this frame would be accidentally encrypted) + if (connection->chacha_ctx && !chacha_ctx) { // Check that encryption is valid, but that it was not set up on this frame (as this frame shouldn't be encrypted as it will be the final /pair-setup) unsigned char auth_tag[16]; - unsigned char nonce[NONCE_LENGTH]; int total_packet_len = 2 + datalen + 16; - // uint16_t payload_len = htons(datalen); unsigned char * encrypted = malloc(total_packet_len); // 2 bytes for payload size; datalen = payload; 16 bytes for authTag memcpy(encrypted, &datalen, 2); - uint64_t counter = connection->encryption_counter; - memcpy(nonce + 4, &counter, NONCE_LENGTH - 4); - - encrypt_chacha(encrypted + 2, data, datalen, connection->encryption_key, connection->encryption_key_len, encrypted, 2, auth_tag, 16, nonce); + encrypt_chacha(encrypted + 2, data, datalen, connection->chacha_ctx, encrypted, 2, auth_tag, 16); memcpy(encrypted + 2 + datalen, auth_tag, 16); data = encrypted; datalen = total_packet_len; - connection->encryption_counter++; } written = 0; diff --git a/lib/mirror_buffer.c b/lib/mirror_buffer.c index b88fe244a..8870fb75f 100644 --- a/lib/mirror_buffer.c +++ b/lib/mirror_buffer.c @@ -31,6 +31,7 @@ struct mirror_buffer_s { logger_t *logger; aes_ctx_t *aes_ctx; + chacha_ctx_t *chacha_ctx; int nextDecryptCount; uint8_t og[16]; /* audio aes key is used in a hash for the video aes key and iv */ @@ -67,6 +68,19 @@ mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, const uint64_t *streamCon mirror_buffer->aes_ctx = aes_ctr_init(aeskey_video, aesiv_video); } +void +mirror_buffer_init_chacha(mirror_buffer_t *mirror_buffer, unsigned char *session_key, const uint64_t *streamConnectionID) +{ + unsigned char stream_key[32]; + int stream_key_len = 32; + char stream_key_salt[16 + 20]; // Needs to be large enough to hold "DataStream-Salt" + streamConnectionID as a str + int ret = snprintf( stream_key_salt, sizeof(stream_key_salt), "%s%llu", "DataStream-Salt", *streamConnectionID ); + + mirror_buffer->chacha_ctx = malloc(sizeof(chacha_ctx_t)); + + chacha_setup_keys(mirror_buffer->chacha_ctx, session_key, stream_key_salt, "DataStream-Output-Encryption-Key", NULL, NULL); +} + mirror_buffer_t * mirror_buffer_init(logger_t *logger, const unsigned char *aeskey) { @@ -82,35 +96,45 @@ mirror_buffer_init(logger_t *logger, const unsigned char *aeskey) return mirror_buffer; } -void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char* input, unsigned char* output, int inputLen) { +void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char packet_header[128], unsigned char* input, unsigned char* output, int *input_len) { + int inputLen = *input_len; // Start decrypting - if (mirror_buffer->nextDecryptCount > 0) {//mirror_buffer->nextDecryptCount = 10 - for (int i = 0; i < mirror_buffer->nextDecryptCount; i++) { - output[i] = (input[i] ^ mirror_buffer->og[(16 - mirror_buffer->nextDecryptCount) + i]); + if (!mirror_buffer->chacha_ctx) { + if (mirror_buffer->nextDecryptCount > 0) {//mirror_buffer->nextDecryptCount = 10 + for (int i = 0; i < mirror_buffer->nextDecryptCount; i++) { + output[i] = (input[i] ^ mirror_buffer->og[(16 - mirror_buffer->nextDecryptCount) + i]); + } + } + // Handling encrypted bytes + int encryptlen = ((inputLen - mirror_buffer->nextDecryptCount) / 16) * 16; + // Aes decryption + aes_ctr_start_fresh_block(mirror_buffer->aes_ctx); + aes_ctr_decrypt(mirror_buffer->aes_ctx, input + mirror_buffer->nextDecryptCount, + input + mirror_buffer->nextDecryptCount, encryptlen); + // Copy to output + memcpy(output + mirror_buffer->nextDecryptCount, input + mirror_buffer->nextDecryptCount, encryptlen); + // int outputlength = mirror_buffer->nextDecryptCount + encryptlen; + // Processing remaining length + int restlen = (inputLen - mirror_buffer->nextDecryptCount) % 16; + int reststart = inputLen - restlen; + mirror_buffer->nextDecryptCount = 0; + if (restlen > 0) { + memset(mirror_buffer->og, 0, 16); + memcpy(mirror_buffer->og, input + reststart, restlen); + aes_ctr_decrypt(mirror_buffer->aes_ctx, mirror_buffer->og, mirror_buffer->og, 16); + for (int j = 0; j < restlen; j++) { + output[reststart + j] = mirror_buffer->og[j]; + } + //outputlength += restlen; + mirror_buffer->nextDecryptCount = 16 - restlen;// Difference 16-6=10 bytes } } - // Handling encrypted bytes - int encryptlen = ((inputLen - mirror_buffer->nextDecryptCount) / 16) * 16; - // Aes decryption - aes_ctr_start_fresh_block(mirror_buffer->aes_ctx); - aes_ctr_decrypt(mirror_buffer->aes_ctx, input + mirror_buffer->nextDecryptCount, - input + mirror_buffer->nextDecryptCount, encryptlen); - // Copy to output - memcpy(output + mirror_buffer->nextDecryptCount, input + mirror_buffer->nextDecryptCount, encryptlen); - // int outputlength = mirror_buffer->nextDecryptCount + encryptlen; - // Processing remaining length - int restlen = (inputLen - mirror_buffer->nextDecryptCount) % 16; - int reststart = inputLen - restlen; - mirror_buffer->nextDecryptCount = 0; - if (restlen > 0) { - memset(mirror_buffer->og, 0, 16); - memcpy(mirror_buffer->og, input + reststart, restlen); - aes_ctr_decrypt(mirror_buffer->aes_ctx, mirror_buffer->og, mirror_buffer->og, 16); - for (int j = 0; j < restlen; j++) { - output[reststart + j] = mirror_buffer->og[j]; + else { + if (decrypt_chacha(output, input, inputLen - 16, mirror_buffer->chacha_ctx, packet_header, 128, input + inputLen - 16, 16)) { + printf("ERROR: Failed to decrypt packet"); } - //outputlength += restlen; - mirror_buffer->nextDecryptCount = 16 - restlen;// Difference 16-6=10 bytes + + *input_len -= 16; } } diff --git a/lib/mirror_buffer.h b/lib/mirror_buffer.h index 2b65ab08a..ebdb2c97e 100644 --- a/lib/mirror_buffer.h +++ b/lib/mirror_buffer.h @@ -26,6 +26,7 @@ typedef struct mirror_buffer_s mirror_buffer_t; mirror_buffer_t *mirror_buffer_init( logger_t *logger, const unsigned char *aeskey); void mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, const uint64_t *streamConnectionID); -void mirror_buffer_decrypt(mirror_buffer_t *raop_mirror, unsigned char* input, unsigned char* output, int datalen); +void mirror_buffer_init_chacha(mirror_buffer_t *mirror_buffer, unsigned char *session_key, const uint64_t *streamConnectionID); +void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char packet_header[128], unsigned char* input, unsigned char* output, int *input_len); void mirror_buffer_destroy(mirror_buffer_t *mirror_buffer); #endif //MIRROR_BUFFER_H diff --git a/lib/pairing.c b/lib/pairing.c index 817728828..47ed84a42 100644 --- a/lib/pairing.c +++ b/lib/pairing.c @@ -227,6 +227,11 @@ pairing_session_get_signature(pairing_session_t *session, unsigned char signatur return 0; } +int +pairing_session_get_session_key(pairing_session_t *session, unsigned char ** session_key) { + *session_key = session->srp->session_key; +} + int pairing_session_finish(pairing_session_t *session, const unsigned char signature[PAIRING_SIG_SIZE]) { @@ -370,7 +375,7 @@ srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_ int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A, - int len_A, unsigned char *proof, int client_proof_len, int proof_len, unsigned char * decryption_key, int * decryption_key_len, unsigned char * encryption_key, int * encryption_key_len) { + int len_A, unsigned char *proof, int client_proof_len, int proof_len) { int authenticated = 0; const unsigned char *B = NULL; const unsigned char *b = session->srp->private_key; @@ -404,12 +409,31 @@ srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigne memcpy(proof, M2, proof_len); srp_verifier_delete(verifier); - hkdf_get_key(verifier, decryption_key, decryption_key_len, PAIR_CONTROL_WRITE); +// { 0, "Control-Salt", "Control-Write-Encryption-Key", "" }, +// { 0, "Control-Salt", "Control-Read-Encryption-Key", "" }, - hkdf_get_key(verifier, encryption_key, encryption_key_len, PAIR_CONTROL_READ); + // hkdf_get_key(session_key, len_K, decryption_key, decryption_key_len, "Control-Salt", "Control-Write-Encryption-Key"); + + // hkdf_get_key(session_key, len_K, encryption_key, encryption_key_len, "Control-Salt", "Control-Read-Encryption-Key"); return 0; } + +// int test_get_stream_key(pairing_session_t *session, uint64_t streamConnectionID) { +// unsigned char streamKey[32]; +// int streamKeyLen = 32; +// char streamKeySalt[16 + 20]; // Needs to be large enough to hold "DataStream-Salt" + streamConnectionID as a str +// int ret = snprintf( streamKeySalt, sizeof(streamKeySalt), "%s%llu", "DataStream-Salt", streamConnectionID ); +// hkdf_get_key(session->srp->session_key, sizeof(session->srp->session_key), streamKey, &streamKeyLen, streamKeySalt, "DataStream-Output-Encryption-Key"); + +// printf("Stream Key:\n\n"); +// for (int i = 0; i < streamKeyLen; i++) { +// printf("%02X ", streamKey[i]); +// } + +// printf("\n\n\n"); +// } + int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk, unsigned char *auth_tag) { @@ -460,6 +484,11 @@ srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, return epk_len; } +// int +// raop_mirroring_get_key(pairing_session_t *session, uint64_t streamConnectionID, unsigned char * inKey, int * in_key_len) { +// hkdf_get_key(session->srp->verifier, in_key, in_key_len);) +// } + void access_client_session_data(pairing_session_t *session, char **username, char **client_pk64, bool *setup) { int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1; setup = &(session->pair_setup); diff --git a/lib/pairing.h b/lib/pairing.h index 24216345b..f8a7c02e9 100644 --- a/lib/pairing.h +++ b/lib/pairing.h @@ -46,6 +46,7 @@ int pairing_session_check_handshake_status(pairing_session_t *session); int pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[X25519_KEY_SIZE], const unsigned char ed_key[ED25519_KEY_SIZE]); int pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[X25519_KEY_SIZE]); +int pairing_session_get_session_key(pairing_session_t *session, unsigned char ** session_key); int random_pin(); int pairing_session_get_signature(pairing_session_t *session, unsigned char signature[PAIRING_SIG_SIZE]); int pairing_session_finish(pairing_session_t *session, const unsigned char signature[PAIRING_SIG_SIZE]); @@ -58,7 +59,7 @@ int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_s int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin, const char **salt, int *len_salt, const char **pk, int *len_pk); int srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigned char *A, - int len_A, unsigned char *proof, int client_proof_len, int proof_len, unsigned char * decryption_key, int * decryption_key_len, unsigned char * encryption_key, int * encryption_key_len); + int len_A, unsigned char *proof, int client_proof_len, int proof_len); int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk, unsigned char *auth_tag); void access_client_session_data(pairing_session_t *session, char **username, char **client_pk, bool *setup); diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index c045e54aa..f7ed6d8ec 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -24,6 +24,7 @@ #include #include #include "http_request.h" +#include "crypto.h" #define AUDIO_SAMPLE_RATE 44100 /* all supported AirPlay audio format use this sample rate */ #define SECOND_IN_USECS 1000000 @@ -310,7 +311,7 @@ raop_handler_pairsetup_pin(raop_conn_t *conn, memcpy(proof, client_proof, (int) client_proof_len); free (client_proof); int ret = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, - (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof), NULL, NULL, NULL, NULL); + (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof)); free (client_pk); plist_free(req_root_node); if (ret < 0) { @@ -452,11 +453,17 @@ raop_handler_pairsetup(raop_conn_t *conn, int encryption_key_len = 32; int ret2 = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, - 384, proof, 0x40, 0x40, decryption_key, &decryption_key_len, encryption_key, &encryption_key_len); + 384, proof, 0x40, 0x40); if (ret2 < 0) { logger_log(conn->raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated) %d", ret2); } + unsigned char *session_key; + pairing_session_get_session_key(conn->session, &session_key); + + chacha_ctx_t *ctx = malloc(sizeof(chacha_ctx_t)); + chacha_setup_keys(ctx, session_key, "Control-Salt", "Control-Write-Encryption-Key", "Control-Salt", "Control-Read-Encryption-Key"); + *response_data = malloc(69); char *res_data2 = *response_data; *response_datalen = 69; @@ -468,7 +475,7 @@ raop_handler_pairsetup(raop_conn_t *conn, res_data2[4] = 0x40; memcpy(&res_data2[5], proof, 0x40); - http_request_begin_encryption(request, decryption_key, decryption_key_len, encryption_key, encryption_key_len); + http_request_begin_encryption(request, ctx); break; } @@ -631,11 +638,14 @@ raop_handler_setup(raop_conn_t *conn, plist_from_bin(data, data_len, &req_root_node); plist_t req_ekey_node = plist_dict_get_item(req_root_node, "ekey"); plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv"); + plist_t test_node = plist_dict_get_item(req_root_node, "timingPort"); // For the response plist_t res_root_node = plist_new_dict(); - if (PLIST_IS_DATA(req_eiv_node) && PLIST_IS_DATA(req_ekey_node)) { + + + if (PLIST_IS_INT(test_node)) { // The first SETUP call that initializes keys and timing unsigned char aesiv[16]; @@ -658,10 +668,10 @@ raop_handler_setup(raop_conn_t *conn, plist_get_string_val(req_model_node, &model); plist_t req_name_node = plist_dict_get_item(req_root_node, "name"); plist_get_string_val(req_name_node, &name); - if (conn->raop->callbacks.report_client_request) { + if (conn->raop->callbacks.report_client_request) { conn->raop->callbacks.report_client_request(conn->raop->callbacks.cls, deviceID, model, name, &admit_client); } - if (admit_client && deviceID && name && conn->raop->callbacks.register_client) { + if (admit_client && deviceID && name && conn->raop->callbacks.register_client) { bool pending_registration; char *client_device_id; char *client_pk; /* encoded as null-terminated base64 string*/ @@ -669,12 +679,12 @@ raop_handler_setup(raop_conn_t *conn, if (pending_registration) { if (client_pk && !strcmp(deviceID, client_device_id)) { conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk, name); - } - } + } + } if (client_pk) { free (client_pk); } - } + } if (deviceID) { free (deviceID); deviceID = NULL; @@ -692,37 +702,37 @@ raop_handler_setup(raop_conn_t *conn, plist_free(res_root_node); plist_free(req_root_node); return; - } - - plist_get_data_val(req_eiv_node, &eiv, &eiv_len); - memcpy(aesiv, eiv, 16); - free(eiv); - logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len); - if (logger_debug) { - char* str = utils_data_to_string(aesiv, 16, 16); - logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str); - free(str); - } + } - char* ekey = NULL; - uint64_t ekey_len = 0; - plist_get_data_val(req_ekey_node, &ekey, &ekey_len); - memcpy(eaeskey,ekey,72); - free(ekey); - logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len); - // eaeskey is 72 bytes, aeskey is 16 bytes - if (logger_debug) { - char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16); - logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str); - free (str); - } - int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey); - logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret); - if (logger_debug) { - char *str = utils_data_to_string(aeskey, 16, 16); - logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str); - free(str); - } + // plist_get_data_val(req_eiv_node, &eiv, &eiv_len); + // memcpy(aesiv, eiv, 16); + // free(eiv); + // logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len); + // if (logger_debug) { + // char* str = utils_data_to_string(aesiv, 16, 16); + // logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str); + // free(str); + // } + + // char* ekey = NULL; + // uint64_t ekey_len = 0; + // plist_get_data_val(req_ekey_node, &ekey, &ekey_len); + // memcpy(eaeskey,ekey,72); + // free(ekey); + // logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len); + // // eaeskey is 72 bytes, aeskey is 16 bytes + // if (logger_debug) { + // char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16); + // logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str); + // free (str); + // } + // int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey); + // logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret); + // if (logger_debug) { + // char *str = utils_data_to_string(aeskey, 16, 16); + // logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str); + // free(str); + // } const char *user_agent = http_request_get_header(request, "User-Agent"); logger_log(conn->raop->logger, LOGGER_INFO, "Client identified as User-Agent: %s", user_agent); @@ -779,26 +789,26 @@ raop_handler_setup(raop_conn_t *conn, logger_log(conn->raop->logger, LOGGER_ERR, "Client specified AirPlay2 \"Remote Control\" protocol\n" " Only AirPlay v1 protocol (using NTP and timing port) is supported"); } - } + } char *timing_protocol = NULL; - timing_protocol_t time_protocol; + timing_protocol_t time_protocol; plist_t req_timing_protocol_node = plist_dict_get_item(req_root_node, "timingProtocol"); plist_get_string_val(req_timing_protocol_node, &timing_protocol); if (timing_protocol) { - int string_len = strlen(timing_protocol); - if (strncmp(timing_protocol, "NTP", string_len) == 0) { - time_protocol = NTP; - } else if (strncmp(timing_protocol, "None", string_len) == 0) { - time_protocol = TP_NONE; - } else { - time_protocol = TP_OTHER; - } - if (time_protocol != NTP) { - logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s," - " but timingProtocol= NTP is required here", timing_protocol); - } - free (timing_protocol); - timing_protocol = NULL; + int string_len = strlen(timing_protocol); + if (strncmp(timing_protocol, "NTP", string_len) == 0) { + time_protocol = NTP; + } else if (strncmp(timing_protocol, "None", string_len) == 0) { + time_protocol = TP_NONE; + } else { + time_protocol = TP_OTHER; + } + if (time_protocol != NTP) { + logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s," + " but timingProtocol= NTP is required here", timing_protocol); + } + free (timing_protocol); + timing_protocol = NULL; } else { logger_log(conn->raop->logger, LOGGER_DEBUG, "Client did not specify timingProtocol," " old protocol without offset will be used"); @@ -806,8 +816,9 @@ raop_handler_setup(raop_conn_t *conn, } uint64_t timing_rport = 0; plist_t req_timing_port_node = plist_dict_get_item(req_root_node, "timingPort"); - if (req_timing_port_node) { - plist_get_uint_val(req_timing_port_node, &timing_rport); + if (req_timing_port_node) + { + plist_get_uint_val(req_timing_port_node, &timing_rport); } if (timing_rport) { logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport); @@ -837,9 +848,10 @@ raop_handler_setup(raop_conn_t *conn, remote, conn->remotelen, aeskey, aesiv); conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, remote, conn->remotelen, aeskey); + printf("RAOP RTP MIRROR: %p", conn->raop_rtp_mirror); - // plist_t res_event_port_node = plist_new_uint(conn->raop->port); - plist_t res_event_port_node = plist_new_uint(0); + // plist_t res_event_port_node = plist_new_uint(conn->raop->port); + plist_t res_event_port_node = plist_new_uint(0); plist_t res_timing_port_node = plist_new_uint(timing_lport); plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node); plist_dict_set_item(res_root_node, "eventPort", res_event_port_node); @@ -867,11 +879,16 @@ raop_handler_setup(raop_conn_t *conn, plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID"); uint64_t stream_connection_id; plist_get_uint_val(stream_id_node, &stream_connection_id); + // test_get_stream_key(conn->session, stream_connection_id); logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption" " key and iv): %llu", stream_connection_id); if (conn->raop_rtp_mirror) { + // raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id); + unsigned char *session_key; + pairing_session_get_session_key(conn->session, &session_key); raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id); + raop_rtp_mirror_init_chacha(conn->raop_rtp_mirror, session_key, &stream_connection_id); raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata); logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully"); } else { diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c index 62b53bbc3..9645b4a91 100644 --- a/lib/raop_rtp_mirror.c +++ b/lib/raop_rtp_mirror.c @@ -38,6 +38,7 @@ #include "stream.h" #include "utils.h" #include "plist/plist.h" +// #include "pairing.h" #ifdef _WIN32 #define CAST (char *) @@ -170,6 +171,12 @@ raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamCon mirror_buffer_init_aes(raop_rtp_mirror->buffer, streamConnectionID); } +void +raop_rtp_mirror_init_chacha(raop_rtp_mirror_t *raop_rtp_mirror, unsigned char *session_key, uint64_t *streamConnectionID) +{ + mirror_buffer_init_chacha(raop_rtp_mirror->buffer, session_key, streamConnectionID); +} + #define RAOP_PACKET_LEN 32768 /** * Mirror @@ -421,7 +428,7 @@ raop_rtp_mirror_thread(void *arg) payload_decrypted = payload_out; } // Decrypt data - mirror_buffer_decrypt(raop_rtp_mirror->buffer, payload, payload_decrypted, payload_size); + mirror_buffer_decrypt(raop_rtp_mirror->buffer, packet, payload, payload_decrypted, &payload_size); // It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte // start code for the NAL Byte-Stream Format. @@ -750,6 +757,8 @@ raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror } //use_ipv6 = 0; + printf("Here 1\n"); + raop_rtp_mirror->mirror_data_lport = *mirror_data_lport; if (raop_rtp_mirror_init_socket(raop_rtp_mirror, use_ipv6) < 0) { logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing socket failed"); diff --git a/lib/raop_rtp_mirror.h b/lib/raop_rtp_mirror.h index 2e7914d61..f83cfb2f5 100644 --- a/lib/raop_rtp_mirror.h +++ b/lib/raop_rtp_mirror.h @@ -28,6 +28,7 @@ typedef struct h264codec_s h264codec_t; raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const char *remote, int remotelen, const unsigned char *aeskey); void raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID); +void raop_rtp_mirror_init_chacha(raop_rtp_mirror_t *raop_rtp_mirror, unsigned char *session_key, uint64_t *streamConnectionID); void raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport, uint8_t show_client_FPS_data); void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror); void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror); diff --git a/lib/srp.c b/lib/srp.c index 308eb0bdd..472ce2aa3 100644 --- a/lib/srp.c +++ b/lib/srp.c @@ -1004,7 +1004,7 @@ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char hkdfExpand(SHA512, prk, SHA512_LEN, info, info_len, okm, okm_len); */ static int -hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, enum pair_keys pair_key) +hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, char * salt, char * info) { // #ifdef CONFIG_OPENSSL #include @@ -1018,11 +1018,11 @@ hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm goto error; if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) <= 0) goto error; - if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)pair_keys_map[pair_key].salt, strlen(pair_keys_map[pair_key].salt)) <= 0) + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)salt, strlen(salt)) <= 0) goto error; if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len) <= 0) goto error; - if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)pair_keys_map[pair_key].info, strlen(pair_keys_map[pair_key].info)) <= 0) + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)info, strlen(info)) <= 0) goto error; if (EVP_PKEY_derive(pctx, okm, &okm_len) <= 0) goto error; @@ -1038,142 +1038,107 @@ hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm // #endif } -int hkdf_get_key(struct SRPVerifier * ver, unsigned char * derived_key, int * derived_key_len, enum pair_keys msg_state) { - int session_key_len; - int ret; - - const unsigned char * session_key = srp_verifier_get_session_key(ver, &session_key_len); - if (!session_key) { - printf("Could not get session key");// - return -1; - } - ret = hkdf_extract_expand(derived_key, *derived_key_len, session_key, session_key_len, msg_state); - if (ret < 0) { - printf("%d", ret); - printf("hkdf derived key error"); - return -2; - } - - printf("%02X ", *derived_key); - printf("%02X ", derived_key[1]); - printf("%02X ", derived_key[2]); - printf("%02X ", derived_key[3]); - - return 0; -} - -int -decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]) -{ - EVP_CIPHER_CTX *ctx; - int len; - - const unsigned char * aad = ad; - - printf("DECRYPTION INFO:"); - - printf("\n\nCipher: (%d)\n", cipher_len); - for (int i = 0; i < cipher_len; i++) { - printf("%02X ", cipher[i]); - } - - printf("\n\nDecryption key (%d)\n", key_len); - for (int i = 0; i < key_len; i++) { - printf("%02X ", key[i]); - } - - printf("\n\nAD (%d)\n", ad_len); - for (int i = 0; i < ad_len; i++) { - printf("%02X ", aad[i]); - } - - printf("\n\nTag (%d)\n", tag_len); - for (int i = 0; i < tag_len; i++) { - printf("%02X ", tag[i]); - } - - printf("\n\nNonce\n"); - for (int i = 0; i < NONCE_LENGTH; i++) { - printf("%02X ", nonce[i]); - } +// int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info) { +// int ret; + +// // const unsigned char * session_key = srp_verifier_get_session_key(ver, &session_key_len); +// // if (!session_key) { +// // printf("Could not get session key");// +// // return -1; +// // } +// ret = hkdf_extract_expand(derived_key, *derived_key_len, session_key, session_key_len, salt, info); +// if (ret < 0) { +// printf("%d", ret); +// printf("hkdf derived key error"); +// return -2; +// } + +// // printf("Session Key Dump:\n\n"); +// // for (int i = 0; i < session_key_len; i++) { +// // printf("%02X ", session_key[i]); +// // } + +// // printf("\n\n\n"); + +// return 0; +// } + +// int +// decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]) +// { +// EVP_CIPHER_CTX *ctx; +// int len; + +// const unsigned char * aad = ad; + +// printf("DECRYPTION INFO:"); + +// printf("\n\nCipher: (%d)\n", cipher_len); +// for (int i = 0; i < cipher_len; i++) { +// printf("%02X ", cipher[i]); +// } + +// printf("\n\nDecryption key (%d)\n", key_len); +// for (int i = 0; i < key_len; i++) { +// printf("%02X ", key[i]); +// } + +// printf("\n\nAD (%d)\n", ad_len); +// for (int i = 0; i < ad_len; i++) { +// printf("%02X ", aad[i]); +// } + +// printf("\n\nTag (%d)\n", tag_len); +// for (int i = 0; i < tag_len; i++) { +// printf("%02X ", tag[i]); +// } + +// printf("\n\nNonce\n"); +// for (int i = 0; i < NONCE_LENGTH; i++) { +// printf("%02X ", nonce[i]); +// } - if (! (ctx = EVP_CIPHER_CTX_new())) { - printf("here 1"); - return -1; - } +// if (! (ctx = EVP_CIPHER_CTX_new())) { +// printf("here 1"); +// return -1; +// } - if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce) != 1) { - printf("here 2"); - goto error; - } +// if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce) != 1) { +// printf("here 2"); +// goto error; +// } - if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary - goto error; +// if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary +// goto error; - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1) - goto error; +// if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1) +// goto error; - if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) { - printf("here 5"); - goto error; - } +// if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) { +// printf("here 5"); +// goto error; +// } - if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1) { - printf("here 6"); - goto error; - } +// if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1) { +// printf("here 6"); +// goto error; +// } - if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1) { - printf("here 7: (%d)", len); - goto error; - } +// if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1) { +// printf("here 7: (%d)", len); +// goto error; +// } - EVP_CIPHER_CTX_free(ctx); - return 0; +// EVP_CIPHER_CTX_free(ctx); +// return 0; - error: - EVP_CIPHER_CTX_free(ctx); - return -1; -} - -int -encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]) -{ - EVP_CIPHER_CTX *ctx; - int len; - - if (! (ctx = EVP_CIPHER_CTX_new())) - return -1; - - if (EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce) != 1) - goto error; - - if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary - goto error; - - if (ad_len > 0 && EVP_EncryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) - goto error; - - if (EVP_EncryptUpdate(ctx, cipher, &len, plain, plain_len) != 1) - goto error; - - assert(len == plain_len); - - if (EVP_EncryptFinal_ex(ctx, NULL, &len) != 1) - goto error; - - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, tag) != 1) - goto error; - - EVP_CIPHER_CTX_free(ctx); - return 0; +// error: +// EVP_CIPHER_CTX_free(ctx); +// return -1; +// } - error: - EVP_CIPHER_CTX_free(ctx); - return -1; -} void get_error() { handle_error("Here"); diff --git a/lib/srp.h b/lib/srp.h index 863b20b75..4d7393709 100644 --- a/lib/srp.h +++ b/lib/srp.h @@ -63,8 +63,6 @@ #include -#define NONCE_LENGTH 12 // 96 bits according to chacha poly1305 - struct SRPVerifier; #if 0 /*begin removed section 1*/ struct SRPUser; @@ -215,11 +213,11 @@ enum pair_keys PAIR_EVENTS_READ, }; -int hkdf_get_key(struct SRPVerifier * ver, unsigned char * derived_key, int * derived_key_len, enum pair_keys msg_state); +// int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info); -int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]); +// int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]); -int encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]); +// int encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]); void get_error(); /*******************************************************************************/ diff --git a/renderers/video_renderer_gstreamer.c b/renderers/video_renderer_gstreamer.c index b8968c329..62aaa66ac 100644 --- a/renderers/video_renderer_gstreamer.c +++ b/renderers/video_renderer_gstreamer.c @@ -24,6 +24,8 @@ #include #include +#include + #define SECOND_IN_NSECS 1000000000UL #ifdef X_DISPLAY_FIX #include @@ -267,6 +269,11 @@ void video_renderer_render_buffer(unsigned char* data, int *data_len, int *nal_c * byte-aligned: the first byte of invalid data (decryption failed) is 0x01 */ if (data[0]) { logger_log(logger, LOGGER_ERR, "*** ERROR decryption of video packet failed "); + + printf("PACKET DUMP (%d): \n", data_len); + for (int i = 0; i < *data_len; i++) { + printf("%02X ", data[i]); + } } else { if (first_packet) { logger_log(logger, LOGGER_INFO, "Begin streaming to GStreamer video pipeline"); From 93c44ccd8e7042e773b14ff14f17ab8f188101a7 Mon Sep 17 00:00:00 2001 From: Connor Date: Sun, 1 Sep 2024 20:14:29 +0100 Subject: [PATCH 4/4] Improved handling for pair-setup scenarios + general cleanup --- lib/crypto.c | 10 +-- lib/crypto.h | 2 - lib/dnssd.c | 2 - lib/httpd.c | 4 + lib/pairing.c | 38 +--------- lib/raop_handlers.h | 36 ++++----- lib/srp.c | 176 -------------------------------------------- lib/srp.h | 27 ------- 8 files changed, 22 insertions(+), 273 deletions(-) diff --git a/lib/crypto.c b/lib/crypto.c index d0a07ff26..60e66793f 100644 --- a/lib/crypto.c +++ b/lib/crypto.c @@ -589,7 +589,7 @@ void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len) hkdfExpand(SHA512, prk, SHA512_LEN, info, info_len, okm, okm_len); */ static int -hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, char * salt, char * info) +hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, uint8_t * salt, uint8_t * info) { EVP_PKEY_CTX *pctx; @@ -601,11 +601,11 @@ hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm goto error; if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) <= 0) goto error; - if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)salt, strlen(salt)) <= 0) + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)salt, strlen((const char *)salt)) <= 0) goto error; if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len) <= 0) goto error; - if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)info, strlen(info)) <= 0) + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)info, strlen((const char *)info)) <= 0) goto error; if (EVP_PKEY_derive(pctx, okm, &okm_len) <= 0) goto error; @@ -618,10 +618,6 @@ hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm return -1; } -int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info) { - return hkdf_extract_expand(derived_key, *derived_key_len, session_key, session_key_len, salt, info); -} - /* Creates the decryption key or the encryption key (or both) for HomeKit-based connections chacha_setup_keys(ctx, "DataStream-Salt123123123", "DataStream-Output-Encryption-Key", NULL, NULL); chacha_setup_keys(ctx, "Events-Salt", "Events-Write-Encryption-Key", "Events-Salt", "Events-Read-Encryption-Key") diff --git a/lib/crypto.h b/lib/crypto.h index 7bfc9732f..b66a0f6b6 100644 --- a/lib/crypto.h +++ b/lib/crypto.h @@ -124,8 +124,6 @@ struct chacha_ctx_s { typedef struct chacha_ctx_s chacha_ctx_t; -int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info); - int chacha_setup_keys(chacha_ctx_t *ctx, unsigned char *session_key, unsigned char *decryption_salt, unsigned char *decryption_info, unsigned char *encryption_salt, unsigned char *encryption_info); int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, chacha_ctx_t *chacha_ctx, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len); diff --git a/lib/dnssd.c b/lib/dnssd.c index 10f167d04..00bbbae1b 100644 --- a/lib/dnssd.c +++ b/lib/dnssd.c @@ -377,8 +377,6 @@ dnssd_register_airplay(dnssd_t *dnssd, unsigned short port) dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS); dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV); - return 0; - /* Register the service */ retval = dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0, dnssd->name, "_airplay._tcp", diff --git a/lib/httpd.c b/lib/httpd.c index 40ddc8c46..9fa0b62c5 100644 --- a/lib/httpd.c +++ b/lib/httpd.c @@ -150,6 +150,10 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection) connection->connected = 0; connection->user_data = NULL; connection->type = CONNECTION_TYPE_UNKNOWN; + if (connection->chacha_ctx) { + free(connection->chacha_ctx); + connection->chacha_ctx = NULL; + } httpd->open_connections--; } diff --git a/lib/pairing.c b/lib/pairing.c index 47ed84a42..0e216a38d 100644 --- a/lib/pairing.c +++ b/lib/pairing.c @@ -314,9 +314,6 @@ random_pin() { int srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_id, const char *pin, const char **salt, int *len_salt, const char **pk, int *len_pk) { - // if (strlen(device_id) > SRP_USERNAME_SIZE) { - // return -1; - // } strncpy(session->username, device_id, 10); @@ -332,9 +329,7 @@ srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_ get_random_bytes(session->srp->private_key, SRP_PRIVATE_KEY_SIZE); const unsigned char *srp_b = session->srp->private_key; - // const unsigned char b_bytes[] = { 0x36, 0xBF, 0x27, 0xC5, 0xD3, 0x38, 0xCA, 0x0A, 0xCA, 0xB0, 0x4E, 0x09, 0xEB, 0x8F, 0x3F, 0x7A, 0x8E, 0xCB, 0x6C, 0xB5, 0x84, 0x01, 0x34, 0xCF, 0x71, 0x4D, 0x2F, 0xB2, 0x61, 0xCF, 0xFA, 0x31 }; - // const unsigned char *srp_b = b_bytes; - // memcpy(session->srp->private_key, b_bytes, 32); + unsigned char * srp_B; unsigned char * srp_s; unsigned char * srp_v; @@ -363,10 +358,6 @@ srp_new_user(pairing_session_t *session, pairing_t *pairing, const char *device_ (const unsigned char **) &srp_B, &len_B, NULL, NULL, 1); - // for (size_t i = 0; i < 384; i++) { - // printf("%02X ", srp_B[i]); - // } - *pk = (char *) srp_B; *len_pk = len_B; @@ -409,31 +400,9 @@ srp_validate_proof(pairing_session_t *session, pairing_t *pairing, const unsigne memcpy(proof, M2, proof_len); srp_verifier_delete(verifier); -// { 0, "Control-Salt", "Control-Write-Encryption-Key", "" }, -// { 0, "Control-Salt", "Control-Read-Encryption-Key", "" }, - - // hkdf_get_key(session_key, len_K, decryption_key, decryption_key_len, "Control-Salt", "Control-Write-Encryption-Key"); - - // hkdf_get_key(session_key, len_K, encryption_key, encryption_key_len, "Control-Salt", "Control-Read-Encryption-Key"); - return 0; } -// int test_get_stream_key(pairing_session_t *session, uint64_t streamConnectionID) { -// unsigned char streamKey[32]; -// int streamKeyLen = 32; -// char streamKeySalt[16 + 20]; // Needs to be large enough to hold "DataStream-Salt" + streamConnectionID as a str -// int ret = snprintf( streamKeySalt, sizeof(streamKeySalt), "%s%llu", "DataStream-Salt", streamConnectionID ); -// hkdf_get_key(session->srp->session_key, sizeof(session->srp->session_key), streamKey, &streamKeyLen, streamKeySalt, "DataStream-Output-Encryption-Key"); - -// printf("Stream Key:\n\n"); -// for (int i = 0; i < streamKeyLen; i++) { -// printf("%02X ", streamKey[i]); -// } - -// printf("\n\n\n"); -// } - int srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, unsigned char *epk, unsigned char *auth_tag) { @@ -484,11 +453,6 @@ srp_confirm_pair_setup(pairing_session_t *session, pairing_t *pairing, return epk_len; } -// int -// raop_mirroring_get_key(pairing_session_t *session, uint64_t streamConnectionID, unsigned char * inKey, int * in_key_len) { -// hkdf_get_key(session->srp->verifier, in_key, in_key_len);) -// } - void access_client_session_data(pairing_session_t *session, char **username, char **client_pk64, bool *setup) { int len64 = 4 * (1 + (ED25519_KEY_SIZE / 3)) + 1; setup = &(session->pair_setup); diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index f7ed6d8ec..05c3b56da 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -388,12 +388,15 @@ raop_handler_pairsetup(raop_conn_t *conn, data = http_request_get_data(request, &datalen); - // if (datalen != 32) { - // logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data"); - // return; - // } - if (datalen == 32) { + char* hkpVersion = http_request_get_header(request, "X-Apple-HKP"); + + if (hkpVersion == NULL) { + if (datalen != 32) { + logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data"); + return; + } + logger_log(conn->raop->logger, LOGGER_DEBUG, "PAIRING: Legacy pairing"); pairing_get_public_key(conn->raop->pairing, public_key); pairing_session_set_setup_status(conn->session); @@ -404,15 +407,14 @@ raop_handler_pairsetup(raop_conn_t *conn, memcpy(*response_data, public_key, sizeof(public_key)); *response_datalen = sizeof(public_key); } - } else { + } + else if (!strcmp(hkpVersion, "4")) { switch (data[2]) { case 0x00: // M1 (usually 0x01, 0x06, 0x00, 0x01) const char *salt; - // char pin[6]; const char *pk; int len_pk, len_salt; char *method = NULL; - // char *user = NULL; int ret = srp_new_user(conn->session, conn->raop->pairing, "Pair-Setup", "3939", &salt, &len_salt, &pk, &len_pk); *response_data = malloc(409); @@ -435,22 +437,9 @@ raop_handler_pairsetup(raop_conn_t *conn, case 0x03: // M3 unsigned char *client_pk = malloc(384); unsigned char proof[64]; - unsigned char *client_proof = malloc(64); memcpy(client_pk, &data[5], 0xff); memcpy(&client_pk[0xff], &data[262], 0x81); - memcpy(client_proof, &data[393], 0x40); - memcpy(proof, client_proof, 0x40); - - // unsigned char original_pk[] = { 0xa4, 0x1c, 0x3e, 0xec, 0x53, 0xbd, 0xd3, 0x99, 0xb5, 0x01, 0xc1, 0x1a, 0xda, 0x62, 0x04, 0x97, 0x43, 0x8e, 0xaf, 0x83, 0x8c, 0x4a, 0x3f, 0xc9, 0x96, 0xfb, 0x83, 0xae, 0x94, 0x0c, 0x94, 0x53, 0xc2, 0xac, 0x89, 0x82, 0x0b, 0xd4, 0x01, 0x38, 0x09, 0xae, 0x5e, 0x1a, 0xdf, 0xf2, 0x16, 0xf5, 0x06, 0xdc, 0x7b, 0x9e, 0x73, 0xc3, 0x90, 0x96, 0xb8, 0x67, 0xe7, 0x31, 0x95, 0x65, 0x3d, 0x4b, 0x17, 0x54, 0xbf, 0xcd, 0x08, 0xc6, 0x6f, 0x16, 0xec, 0x64, 0x05, 0xcf, 0x45, 0xae, 0x72, 0xbe, 0xef, 0xd4, 0x15, 0x0d, 0x86, 0x48, 0xc8, 0x11, 0xda, 0x1f, 0xde, 0xe0, 0xca, 0xda, 0x3a, 0x72, 0x44, 0x6d, 0xd5, 0x22, 0x40, 0x29, 0x00, 0x66, 0xbf, 0x16, 0x6a, 0x7f, 0x0c, 0x51, 0x37, 0x9d, 0xfd, 0xbd, 0xcd, 0x0c, 0x25, 0x1a, 0x21, 0xae, 0xd7, 0x1f, 0x07, 0xbf, 0xe4, 0xf4, 0x4d, 0x62, 0x69, 0x91, 0x7c, 0x6c, 0xa1, 0x8a, 0xcb, 0x4e, 0xc7, 0x0c, 0x6f, 0xf9, 0xbb, 0x1d, 0xeb, 0x07, 0x2f, 0xde, 0x0b, 0x15, 0xf4, 0xf7, 0xc1, 0xa7, 0xe2, 0x07, 0x54, 0x96, 0x7b, 0xea, 0x3e, 0x3f, 0x6c, 0xdc, 0xcc, 0x15, 0xf1, 0x5e, 0x94, 0x13, 0xc8, 0xca, 0x2f, 0xa5, 0xc9, 0xc0, 0x97, 0x34, 0xd5, 0xd4, 0x82, 0xac, 0xcb, 0x6b, 0x14, 0x00, 0x4a, 0x3a, 0x75, 0xb8, 0x6c, 0x73, 0x46, 0xdf, 0xb7, 0x04, 0x76, 0x1d, 0x52, 0xcc, 0x84, 0xe8, 0xda, 0x17, 0x49, 0xf1, 0x5b, 0x02, 0x43, 0xde, 0x7b, 0x28, 0xa7, 0x9d, 0xa8, 0xc0, 0xf3, 0x9f, 0x3e, 0x96, 0x41, 0x33, 0xa7, 0xb5, 0xe4, 0x40, 0x03, 0xe9, 0x53, 0x05, 0x05, 0x5c, 0x6c, 0xd4, 0x3c, 0xb1, 0xcc, 0x07, 0x0b, 0xeb, 0xbc, 0xce, 0x78, 0x45, 0x73, 0x68, 0xed, 0xba, 0xa1, 0x77, 0x17, 0x7a, 0x0e, 0xf0, 0xb8, 0x3e, 0x10, 0x97, 0xce, 0x2e, 0x31, 0x91, 0x4b, 0xcf, 0x40, 0xf7, 0x6d, 0x87, 0xa6, 0x3f, 0x72, 0xc1, 0xbb, 0x90, 0x58, 0x51, 0x4d, 0x6b, 0x1a, 0x64, 0x46, 0xc7, 0xc1, 0xc6, 0x11, 0xf4, 0xf5, 0xff, 0xe5, 0xcf, 0xae, 0xd9, 0x74, 0xaf, 0x65, 0x41, 0xa0, 0x80, 0xc5, 0xc0, 0x36, 0x58, 0x41, 0x94, 0xb7, 0xd9, 0xe0, 0x52, 0x35, 0xb3, 0xe4, 0x0a, 0xd8, 0x35, 0xfd, 0x49, 0xbf, 0x7c, 0x33, 0x9b, 0x40, 0x2d, 0x02, 0x4b, 0xd9, 0x6e, 0xc0, 0x96, 0x01, 0x8b, 0xd6, 0x5f, 0x34, 0xce, 0xd5, 0xc5, 0x90, 0x68, 0x11, 0x67, 0x60, 0xa4, 0x99, 0x48, 0x9c, 0x2c, 0xb2, 0x64, 0x25, 0x50, 0x25, 0x5e, 0xe7, 0x56, 0x6d, 0x9a, 0x6c, 0x1a, 0x5b, 0x15, 0x4c, 0xe2, 0xed, 0x4f, 0x6a, 0xb9, 0x31, 0x18, 0x6e, 0x91, 0xb3, 0x8a, 0xed, 0xab, 0x31, 0x40, 0x0f, 0xae, 0xbd, 0x58, 0x0b, 0x79, 0xed, 0xc5, 0x8d, 0x14 }; - // client_pk = original_pk; - - // unsigned char original_proof[] = { 0x7a, 0x00, 0xef, 0x8b, 0xd8, 0x4d, 0xb0, 0x99, 0x4a, 0x74, 0x94, 0x22, 0x49, 0x66, 0x03, 0xfb, 0xf9, 0x66, 0x63, 0x68, 0x83, 0x78, 0x05, 0x6e, 0xcf, 0x4a, 0xab, 0x2c, 0x1f, 0x82, 0x9a, 0xe4, 0xc3, 0xb3, 0x0d, 0x7f, 0xc6, 0x30, 0xb3, 0x29, 0x69, 0xd4, 0xec, 0xc8, 0xc2, 0x7c, 0x17, 0x47, 0x82, 0xe1, 0x54, 0x15, 0x91, 0x7d, 0x1f, 0xbe, 0xca, 0xd3, 0xaf, 0xfd, 0xda, 0x5f, 0x6b, 0x8f }; - // memcpy(proof, original_proof, 0x40); - - unsigned char * decryption_key = malloc(32); - int decryption_key_len = 32; - unsigned char * encryption_key = malloc(32); - int encryption_key_len = 32; + memcpy(proof, &data[393], 0x40); int ret2 = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, 384, proof, 0x40, 0x40); @@ -480,6 +469,9 @@ raop_handler_pairsetup(raop_conn_t *conn, break; } } + else { + logger_log(conn->raop->logger, LOGGER_ERR, "PAIRING: Unimplemented HomeKit pairing version: %s", hkpVersion); + } } static void diff --git a/lib/srp.c b/lib/srp.c index 472ce2aa3..df206f973 100644 --- a/lib/srp.c +++ b/lib/srp.c @@ -206,41 +206,6 @@ static struct NGHex global_Ng_constants[] = { {0,0} /* null sentinel */ }; -struct pair_keys_map -{ - uint8_t state; - const char *salt; - const char *info; - const char nonce[8]; -}; - -static struct pair_keys_map pair_keys_map[] = -{ - // Used for /pair-setup - { 0x01, NULL, NULL, "" }, - { 0x02, NULL, NULL, "" }, - { 0x03, NULL, NULL, "" }, - { 0x04, NULL, NULL, "" }, - { 0x05, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg05" }, - { 0x06, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg06" }, - { 0, "Pair-Setup-Controller-Sign-Salt", "Pair-Setup-Controller-Sign-Info", "" }, - { 0, "Pair-Setup-Accessory-Sign-Salt", "Pair-Setup-Accessory-Sign-Info", "" }, - - // Used for /pair-verify - { 0x01, NULL, NULL, "" }, - { 0x02, "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", "PV-Msg02" }, - { 0x03, "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", "PV-Msg03" }, - { 0x04, NULL, NULL, "" }, - - // Encryption/decryption of control channel - { 0, "Control-Salt", "Control-Write-Encryption-Key", "" }, - { 0, "Control-Salt", "Control-Read-Encryption-Key", "" }, - - // Encryption/decryption of event channel - { 0, "Events-Salt", "Events-Write-Encryption-Key", "" }, - { 0, "Events-Salt", "Events-Read-Encryption-Key", "" }, -}; - static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex ) { NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) ); @@ -998,147 +963,6 @@ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char *bytes_HAMK = NULL; } -/* Executes SHA512 RFC 5869 extract + expand, writing a derived key to okm - - hkdfExtract(SHA512, salt, salt_len, ikm, ikm_len, prk); - hkdfExpand(SHA512, prk, SHA512_LEN, info, info_len, okm, okm_len); -*/ -static int -hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, char * salt, char * info) -{ -// #ifdef CONFIG_OPENSSL -#include - EVP_PKEY_CTX *pctx; - - if (okm_len > SHA512_DIGEST_LENGTH) - return -1; - if (! (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL))) - return -1; - if (EVP_PKEY_derive_init(pctx) <= 0) - goto error; - if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) <= 0) - goto error; - if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)salt, strlen(salt)) <= 0) - goto error; - if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len) <= 0) - goto error; - if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)info, strlen(info)) <= 0) - goto error; - if (EVP_PKEY_derive(pctx, okm, &okm_len) <= 0) - goto error; - - EVP_PKEY_CTX_free(pctx); - return 0; - - error: - EVP_PKEY_CTX_free(pctx); - return -1; -// #else -// return -1; -// #endif -} - -// int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info) { -// int ret; - -// // const unsigned char * session_key = srp_verifier_get_session_key(ver, &session_key_len); -// // if (!session_key) { -// // printf("Could not get session key");// -// // return -1; -// // } -// ret = hkdf_extract_expand(derived_key, *derived_key_len, session_key, session_key_len, salt, info); -// if (ret < 0) { -// printf("%d", ret); -// printf("hkdf derived key error"); -// return -2; -// } - -// // printf("Session Key Dump:\n\n"); -// // for (int i = 0; i < session_key_len; i++) { -// // printf("%02X ", session_key[i]); -// // } - -// // printf("\n\n\n"); - -// return 0; -// } - -// int -// decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]) -// { -// EVP_CIPHER_CTX *ctx; -// int len; - -// const unsigned char * aad = ad; - -// printf("DECRYPTION INFO:"); - -// printf("\n\nCipher: (%d)\n", cipher_len); -// for (int i = 0; i < cipher_len; i++) { -// printf("%02X ", cipher[i]); -// } - -// printf("\n\nDecryption key (%d)\n", key_len); -// for (int i = 0; i < key_len; i++) { -// printf("%02X ", key[i]); -// } - -// printf("\n\nAD (%d)\n", ad_len); -// for (int i = 0; i < ad_len; i++) { -// printf("%02X ", aad[i]); -// } - -// printf("\n\nTag (%d)\n", tag_len); -// for (int i = 0; i < tag_len; i++) { -// printf("%02X ", tag[i]); -// } - -// printf("\n\nNonce\n"); -// for (int i = 0; i < NONCE_LENGTH; i++) { -// printf("%02X ", nonce[i]); -// } - - - -// if (! (ctx = EVP_CIPHER_CTX_new())) { -// printf("here 1"); -// return -1; -// } - -// if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce) != 1) { -// printf("here 2"); -// goto error; -// } - -// if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary -// goto error; - -// if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1) -// goto error; - -// if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) { -// printf("here 5"); -// goto error; -// } - -// if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1) { -// printf("here 6"); -// goto error; -// } - -// if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1) { -// printf("here 7: (%d)", len); -// goto error; -// } - -// EVP_CIPHER_CTX_free(ctx); -// return 0; - -// error: -// EVP_CIPHER_CTX_free(ctx); -// return -1; -// } - void get_error() { handle_error("Here"); diff --git a/lib/srp.h b/lib/srp.h index 4d7393709..1e24dbed4 100644 --- a/lib/srp.h +++ b/lib/srp.h @@ -192,33 +192,6 @@ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK ); - -enum pair_keys -{ - PAIR_SETUP_MSG01 = 0, - PAIR_SETUP_MSG02, - PAIR_SETUP_MSG03, - PAIR_SETUP_MSG04, - PAIR_SETUP_MSG05, - PAIR_SETUP_MSG06, - PAIR_SETUP_CONTROLLER_SIGN, - PAIR_SETUP_ACCESSORY_SIGN, - PAIR_VERIFY_MSG01, - PAIR_VERIFY_MSG02, - PAIR_VERIFY_MSG03, - PAIR_VERIFY_MSG04, - PAIR_CONTROL_WRITE, - PAIR_CONTROL_READ, - PAIR_EVENTS_WRITE, - PAIR_EVENTS_READ, -}; - -// int hkdf_get_key(unsigned char * session_key, int session_key_len, unsigned char * derived_key, int * derived_key_len, char * salt, char * info); - -// int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, const uint8_t *key, uint8_t key_len, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len, const uint8_t nonce[NONCE_LENGTH]); - -// int encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]); - void get_error(); /*******************************************************************************/