diff --git a/lib/dnssdint.h b/lib/dnssdint.h index 1c1abec..9a4dbc3 100644 --- a/lib/dnssdint.h +++ b/lib/dnssdint.h @@ -24,7 +24,8 @@ #define RAOP_CN "0,1,2,3" /* Audio codec: PCM, ALAC, AAC, AAC ELD */ #define RAOP_ET "0,3,5" /* Encryption type: None, FairPlay, FairPlay SAPv2.5 */ #define RAOP_VV "2" -#define FEATURES_1 "0x5A7FFEE6" /* first 32 bits of features */ +#define FEATURES_1 "0x5A7FFEE6" /* first 32 bits of features, with bit 27 ("supports legacy pairing") ON */ +//#define FEATURES_1 "0x527FFEE6" /* first 32 bits of features, with bit 27 ("supports legacy pairing") OFF */ #define FEATURES_2 "0x0" /* second 32 bits of features */ #define RAOP_FT FEATURES_1 "," FEATURES_2 #define RAOP_RHD "5.6.0.0" diff --git a/lib/pairing.c b/lib/pairing.c index 1ffdae9..5a0a3b3 100644 --- a/lib/pairing.c +++ b/lib/pairing.c @@ -90,14 +90,19 @@ pairing_get_public_key(pairing_t *pairing, unsigned char public_key[ED25519_KEY_ ed25519_key_get_raw(public_key, pairing->ed); } -void +int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]) { assert(session); - memcpy(ecdh_secret, session->ecdh_secret, X25519_KEY_SIZE); + switch (session->status) { + case STATUS_INITIAL: + return 0; + default: + memcpy(ecdh_secret, session->ecdh_secret, X25519_KEY_SIZE); + return 1; + } } - pairing_session_t * pairing_session_init(pairing_t *pairing) { diff --git a/lib/pairing.h b/lib/pairing.h index 57ef369..e57a9c8 100644 --- a/lib/pairing.h +++ b/lib/pairing.h @@ -38,6 +38,6 @@ void pairing_session_destroy(pairing_session_t *session); void pairing_destroy(pairing_t *pairing); -void pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]); +int pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[X25519_KEY_SIZE]); #endif diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index 48af4d1..db53965 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -395,14 +395,6 @@ raop_handler_setup(raop_conn_t *conn, free(str); } - unsigned char ecdh_secret[X25519_KEY_SIZE]; - pairing_get_ecdh_secret_key(conn->pairing, ecdh_secret); - if (logger_debug) { - char *str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16); - logger_log(conn->raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\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); @@ -413,17 +405,33 @@ raop_handler_setup(raop_conn_t *conn, if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */ logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)"); } else { - memcpy(eaeskey, aeskey, 16); - sha_ctx_t *ctx = sha_init(); - sha_update(ctx, eaeskey, 16); - sha_update(ctx, ecdh_secret, 32); - sha_final(ctx, eaeskey, NULL); - sha_destroy(ctx); - memcpy(aeskey, eaeskey, 16); - if (logger_debug) { - char *str = utils_data_to_string(aeskey, 16, 16); - logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str); - free(str); + unsigned char ecdh_secret[X25519_KEY_SIZE]; + if (pairing_get_ecdh_secret_key(conn->pairing, ecdh_secret)) { + /* In this case (legacy) pairing with client was successfully set up and created the shared ecdh_secret: + * aeskey must be hashed with it + * + * If byte 27 of features ("supports legacy pairing") is turned off, the client does not request pairsetup + * and does NOT set up pairing (this eliminates a 5 second delay in connecting with no apparent bad effects). + * This may be because uxplay currently does not support connections with more than one client at a time + * while AppleTV supports up to 12 clients, and uses pairing to give each a distinct SessionID .*/ + + if (logger_debug) { + char *str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\n%s", str); + free(str); + } + memcpy(eaeskey, aeskey, 16); + sha_ctx_t *ctx = sha_init(); + sha_update(ctx, eaeskey, 16); + sha_update(ctx, ecdh_secret, 32); + sha_final(ctx, eaeskey, NULL); + sha_destroy(ctx); + memcpy(aeskey, eaeskey, 16); + if (logger_debug) { + char *str = utils_data_to_string(aeskey, 16, 16); + logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str); + free(str); + } } }