From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 8 Dec 2021 11:59:09 +0000 (+0000) Subject: Add update to ejurgensen's pair_ap library. Thanks as always. X-Git-Tag: 4.1-rc1~24^2~362^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=429c5dc002b5fcf23a2fe920e1697dc03208a1eb;p=thirdparty%2Fshairport-sync.git Add update to ejurgensen's pair_ap library. Thanks as always. --- diff --git a/pair_ap/client-example.c b/pair_ap/client-example.c index 1cb766bb..c5736ed5 100644 --- a/pair_ap/client-example.c +++ b/pair_ap/client-example.c @@ -108,7 +108,7 @@ make_request(const char *url, const void *data, size_t len, const char *content_ else if (pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT) evrtsp_add_header(req->output_headers, "X-Apple-HKP", "4"); - printf("Making request %d to '%s'... ", cseq, url); + printf("Making request %d to '%s' (len %zu)... ", cseq, url, len); return evrtsp_make_request(evcon, req, EVRTSP_REQ_POST, url); } @@ -136,7 +136,7 @@ make_request_options(const char *url, const void *data, size_t len, const char * // evrtsp_add_header(req->output_headers, "Active-Remote", ACTIVE_REMOTE); evrtsp_add_header(req->output_headers, "X-Apple-HKP", "3"); - printf("Making request %d to '%s'... ", cseq, url); + printf("Making request %d to '%s' (len %zu)... ", cseq, url, len); return evrtsp_make_request(evcon, req, EVRTSP_REQ_OPTIONS, url); } diff --git a/pair_ap/pair-internal.h b/pair_ap/pair-internal.h index f9d8fa66..3de74daa 100644 --- a/pair_ap/pair-internal.h +++ b/pair_ap/pair-internal.h @@ -367,7 +367,7 @@ int hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_len, const unsigned char *m2, int m2_len); bnum -H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2); +H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2, int padded_len); bnum H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes); @@ -384,4 +384,7 @@ hash_num(enum hash_alg alg, const bnum n, unsigned char *dest); #ifdef DEBUG_PAIR void hexdump(const char *msg, uint8_t *mem, size_t len); + +void +bnum_dump(const char *msg, bnum n); #endif diff --git a/pair_ap/pair.c b/pair_ap/pair.c index 2b58bbe1..e444ab5b 100644 --- a/pair_ap/pair.c +++ b/pair_ap/pair.c @@ -25,6 +25,7 @@ #include #include #include // for isprint() +#include #include @@ -188,24 +189,27 @@ hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_le return hash_final(alg, &ctx, md); } +// See rfc5054 PAD() bnum -H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2) +H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2, int padded_len) { bnum bn; unsigned char *bin; unsigned char buff[SHA512_DIGEST_LENGTH]; int len_n1 = bnum_num_bytes(n1); int len_n2 = bnum_num_bytes(n2); - int nbytes = 2 * len_n1; + int nbytes = 2 * padded_len; + int offset_n1 = padded_len - len_n1; + int offset_n2 = nbytes - len_n2; - if ((len_n2 < 1) || (len_n2 > len_n1)) - return 0; + assert(len_n1 <= padded_len); + assert(len_n2 <= padded_len); - bin = calloc( 1, nbytes ); + bin = calloc(1, nbytes); - bnum_bn2bin(n1, bin, len_n1); - bnum_bn2bin(n2, bin + nbytes - len_n2, len_n2); - hash( alg, bin, nbytes, buff ); + bnum_bn2bin(n1, bin + offset_n1, len_n1); + bnum_bn2bin(n2, bin + offset_n2, len_n2); + hash(alg, bin, nbytes, buff); free(bin); bnum_bin2bn(bn, buff, hash_length(alg)); return bn; @@ -221,8 +225,8 @@ H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes) unsigned char *bin = malloc(nbytes); bnum_bn2bin(n, bin, len_n); - memcpy( bin + len_n, bytes, len_bytes ); - hash( alg, bin, nbytes, buff ); + memcpy(bin + len_n, bytes, len_bytes); + hash(alg, bin, nbytes, buff); free(bin); bnum_bin2bn(bn, buff, hash_length(alg)); return bn; @@ -289,6 +293,16 @@ hexdump(const char *msg, uint8_t *mem, size_t len) } } } + +void +bnum_dump(const char *msg, bnum n) +{ + int len_n = bnum_num_bytes(n); + uint8_t *bin = calloc(1, len_n); + bnum_bn2bin(n, bin, len_n); + hexdump(msg, bin, len_n); + free(bin); +} #endif diff --git a/pair_ap/pair.h b/pair_ap/pair.h index d3be0ca9..35a5aeb8 100644 --- a/pair_ap/pair.h +++ b/pair_ap/pair.h @@ -4,7 +4,7 @@ #include #define PAIR_AP_VERSION_MAJOR 0 -#define PAIR_AP_VERSION_MINOR 10 +#define PAIR_AP_VERSION_MINOR 12 #define PAIR_AP_DEVICE_ID_LEN_MAX 64 diff --git a/pair_ap/pair_fruit.c b/pair_ap/pair_fruit.c index 3236faf5..86f23636 100644 --- a/pair_ap/pair_fruit.c +++ b/pair_ap/pair_fruit.c @@ -60,6 +60,7 @@ typedef struct { bnum N; bnum g; + int N_len; } NGConstant; struct SRPUser @@ -122,6 +123,8 @@ new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex) bnum_hex2bn(ng->N, n_hex); bnum_hex2bn(ng->g, g_hex); + ng->N_len = bnum_num_bytes(ng->N); + return ng; } @@ -348,7 +351,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in bnum_bin2bn(s, bytes_s, len_s); bnum_bin2bn(B, bytes_B, len_B); - k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g); + k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g, usr->ng->N_len); bnum_new(v); bnum_new(tmp1); bnum_new(tmp2); @@ -357,7 +360,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in if (!s || !B || !k || !v || !tmp1 || !tmp2 || !tmp3) goto cleanup1; - u = H_nn_pad(usr->alg, usr->A, B); + u = H_nn_pad(usr->alg, usr->A, B, usr->ng->N_len); x = calculate_x(usr->alg, s, usr->username, usr->password, usr->password_len); if (!u || !x) goto cleanup2; diff --git a/pair_ap/pair_homekit.c b/pair_ap/pair_homekit.c index 6540d42d..d08baca2 100644 --- a/pair_ap/pair_homekit.c +++ b/pair_ap/pair_homekit.c @@ -55,6 +55,8 @@ #define REQUEST_BUFSIZE 4096 #define ENCRYPTED_LEN_MAX 0x400 +// #define DEBUG_SHORT_A 1 + enum pair_keys { PAIR_SETUP_MSG01 = 0, @@ -140,6 +142,7 @@ typedef enum typedef struct { + int N_len; bnum N; bnum g; } NGConstant; @@ -184,6 +187,7 @@ struct SRPVerifier struct NGHex { + int N_len; const char *n_hex; const char *g_hex; }; @@ -192,6 +196,7 @@ struct NGHex static struct NGHex global_Ng_constants[] = { { /* 2048 */ + 256, "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4" "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60" "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF" @@ -202,6 +207,7 @@ static struct NGHex global_Ng_constants[] = "2" }, { /* 3072 */ + 384, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B" "139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485" "B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1F" @@ -215,7 +221,7 @@ static struct NGHex global_Ng_constants[] = "D120A93AD2CAFFFFFFFFFFFFFFFF", "5" }, - {0,0} /* null sentinel */ + {0} /* null sentinel */ }; @@ -226,13 +232,17 @@ new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex) if ( ng_type != SRP_NG_CUSTOM ) { - n_hex = global_Ng_constants[ ng_type ].n_hex; - g_hex = global_Ng_constants[ ng_type ].g_hex; + n_hex = global_Ng_constants[ng_type].n_hex; + g_hex = global_Ng_constants[ng_type].g_hex; } bnum_hex2bn(ng->N, n_hex); bnum_hex2bn(ng->g, g_hex); + ng->N_len = bnum_num_bytes(ng->N); + + assert(ng_type == SRP_NG_CUSTOM || ng->N_len == global_Ng_constants[ng_type].N_len); + return ng; } @@ -247,6 +257,15 @@ free_ng(NGConstant * ng) free(ng); } +static int +N_len(SRP_NGType ng_type) +{ + if (ng_type == SRP_NG_CUSTOM) + return -1; + + return global_Ng_constants[ng_type].N_len; +} + static bnum calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsigned char *password, int password_len) { @@ -401,13 +420,27 @@ srp_user_get_session_key(struct SRPUser *usr, int *key_length) return usr->session_key; } +#ifdef DEBUG_SHORT_A +// This value of "a" will yield a 383 byte A +static uint8_t short_a[] = { + 0xef, 0xb5, 0x93, 0xf5, 0x03, 0x97, 0x69, 0x8e, 0x15, 0xed, 0xee, 0x5b, 0xf2, 0xf9, 0x23, 0x6c, + 0xf0, 0x59, 0x6c, 0xe2, 0x77, 0xf2, 0x14, 0x16, 0xac, 0x99, 0xfa, 0x31, 0xae, 0x2b, 0xd3, 0x41, +}; +#endif + /* Output: username, bytes_A, len_A */ static void srp_user_start_authentication(struct SRPUser *usr, const char **username, const unsigned char **bytes_A, int *len_A) { - bnum_random(usr->a, 256); // BN_hex2bn(&(usr->a), "D929DFB605687233C9E9030C2280156D03BDB9FDCF3CCE3BC27D9CCFCB5FF6A1"); + bnum_random(usr->a, 256); +#ifdef DEBUG_SHORT_A + bnum_bin2bn(usr->a, short_a, sizeof(short_a)); +#endif +#ifdef DEBUG_PAIR + bnum_dump("Random value of usr->a:\n", usr->a); +#endif bnum_modexp(usr->A, usr->ng->g, usr->a, usr->ng->N); @@ -443,7 +476,8 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in bnum_bin2bn(s, bytes_s, len_s); bnum_bin2bn(B, bytes_B, len_B); - k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g); + + k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g, usr->ng->N_len); bnum_new(v); bnum_new(tmp1); @@ -453,7 +487,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in if (!s || !B || !k || !v || !tmp1 || !tmp2 || !tmp3) goto cleanup1; - u = H_nn_pad(usr->alg, usr->A, B); + u = H_nn_pad(usr->alg, usr->A, B, usr->ng->N_len); x = calculate_x(usr->alg, s, usr->username, usr->password, usr->password_len); if (!u || !x) goto cleanup2; @@ -535,6 +569,9 @@ srp_create_salted_verification_key(enum hash_alg alg, goto error; bnum_random(s, 128); // MODIFIED from csrp's BN_rand(s, 32, -1, 0) +#ifdef DEBUG_PAIR + bnum_dump("Random value of s:\n", s); +#endif x = calculate_x(alg, s, username, password, len_password); if (!x) @@ -599,8 +636,11 @@ srp_verifier_start_authentication(enum hash_alg alg, SRP_NGType ng_type, bnum_bin2bn(v, bytes_v, len_v); bnum_random(b, 256); // MODIFIED from BN_rand(b, 256, -1, 0) +#ifdef DEBUG_PAIR + bnum_dump("Random value of b:\n", b); +#endif - k = H_nn_pad(alg, ng->N, ng->g); // MODIFIED from H_nn(alg, ng->N, ng->g) + k = H_nn_pad(alg, ng->N, ng->g, ng->N_len); // MODIFIED from H_nn(alg, ng->N, ng->g) if (!k) goto error; @@ -708,8 +748,8 @@ srp_verifier_new(enum hash_alg alg, SRP_NGType ng_type, const char *username, if (bnum_is_zero(tmp1)) goto error; - k = H_nn_pad(alg, ng->N, ng->g); // MODIFIED from H_nn(alg, ng->N, ng->g) - u = H_nn_pad(alg, A, B); // MODIFIED from H_nn(alg, A, B) + k = H_nn_pad(alg, ng->N, ng->g, ng->N_len); // MODIFIED from H_nn(alg, ng->N, ng->g) + u = H_nn_pad(alg, A, B, ng->N_len); // MODIFIED from H_nn(alg, A, B) // S = (A *(v^u)) ^ b bnum_modexp(tmp1, v, u, ng->N); @@ -1163,6 +1203,11 @@ client_setup_new(struct pair_setup_context *handle, const char *pin, pair_cb add crypto_sign_keypair(sctx->public_key, sctx->private_key); +#ifdef DEBUG_PAIR + hexdump("Client public key:\n", sctx->public_key, sizeof(sctx->public_key)); + hexdump("Client private key:\n", sctx->private_key, sizeof(sctx->private_key)); +#endif + return 0; } @@ -1397,18 +1442,24 @@ client_setup_response1(struct pair_setup_context *handle, const uint8_t *data, s } pk = pair_tlv_get_value(response, TLVType_PublicKey); + if (!pk || pk->size > N_len(SRP_NG_3072)) // max 384 bytes + { + handle->errmsg = "Setup response 1: Missing or invalid public key"; + goto error; + } + salt = pair_tlv_get_value(response, TLVType_Salt); - if (!pk || !salt) + if (!salt || salt->size != 16) { - handle->errmsg = "Setup response 1: Missing or invalid pk/salt"; + handle->errmsg = "Setup response 1: Missing or invalid salt"; goto error; } - sctx->pkB_len = pk->size; // 384 + sctx->pkB_len = pk->size; sctx->pkB = malloc(sctx->pkB_len); memcpy(sctx->pkB, pk->value, sctx->pkB_len); - sctx->salt_len = salt->size; // 16 + sctx->salt_len = salt->size; sctx->salt = malloc(sctx->salt_len); memcpy(sctx->salt, salt->value, sctx->salt_len); @@ -1436,13 +1487,13 @@ client_setup_response2(struct pair_setup_context *handle, const uint8_t *data, s } proof = pair_tlv_get_value(response, TLVType_Proof); - if (!proof) + if (!proof || proof->size != SHA512_DIGEST_LENGTH) { - handle->errmsg = "Setup response 2: Missing proof"; + handle->errmsg = "Setup response 2: Missing or invalid proof"; goto error; } - sctx->M2_len = proof->size; // 64 + sctx->M2_len = proof->size; sctx->M2 = malloc(sctx->M2_len); memcpy(sctx->M2, proof->value, sctx->M2_len); @@ -2050,17 +2101,22 @@ server_setup_request2(struct pair_setup_context *handle, const uint8_t *data, si } pk = pair_tlv_get_value(request, TLVType_PublicKey); + if (!pk || pk->size > N_len(SRP_NG_3072)) // 384 bytes or less + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 2: Missing og invalid public key"); + } + proof = pair_tlv_get_value(request, TLVType_Proof); - if (!pk || !proof) + if (!proof || proof->size != SHA512_DIGEST_LENGTH) { - RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 2: Missing pkA or proof"); + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 2: Missing or invalid proof"); } - sctx->pkA_len = pk->size; // 384 + sctx->pkA_len = pk->size; sctx->pkA = malloc(sctx->pkA_len); memcpy(sctx->pkA, pk->value, sctx->pkA_len); - sctx->M1_len = proof->size; // 64 + sctx->M1_len = proof->size; sctx->M1 = malloc(sctx->M1_len); memcpy(sctx->M1, proof->value, sctx->M1_len); @@ -2072,7 +2128,7 @@ server_setup_request2(struct pair_setup_context *handle, const uint8_t *data, si goto out; } - sctx->M2_len = 64; // 512 bit hash + sctx->M2_len = SHA512_DIGEST_LENGTH; srp_verifier_verify_session(sctx->verifier, sctx->M1, &sctx->M2); if (!sctx->M2) { diff --git a/rtsp.c b/rtsp.c index e350b5a4..54f3b73a 100644 --- a/rtsp.c +++ b/rtsp.c @@ -526,7 +526,7 @@ void release_hold_on_play_lock(__attribute__((unused)) rtsp_conn_info *conn) { } void release_play_lock(rtsp_conn_info *conn) { - debug(1, "Connection %d: release play lock.", conn->connection_number); + debug(2, "Connection %d: release play lock.", conn->connection_number); debug_mutex_lock(&playing_conn_lock, 1000000, 3); if (playing_conn == conn) { // if we have the player playing_conn = NULL; // let it go @@ -536,7 +536,7 @@ void release_play_lock(rtsp_conn_info *conn) { } int get_play_lock(rtsp_conn_info *conn) { - debug(1, "Connection %d: request play lock.", conn->connection_number); + debug(2, "Connection %d: request play lock.", conn->connection_number); // returns -1 if it failed, 0 if it succeeded and 1 if it succeeded but // interrupted an existing session int response = 0; @@ -592,16 +592,16 @@ int get_play_lock(rtsp_conn_info *conn) { } if ((have_the_player == 1) && (interrupting_current_session == 1)) { - debug(1, "Connection %d: Got player lock", conn->connection_number); + debug(2, "Connection %d: Got player lock", conn->connection_number); response = 1; } else { - debug(1, "Connection %d: failed to get player lock", conn->connection_number); + debug(1, "Connection %d: failed to get player lock after waiting.", conn->connection_number); response = -1; } } - if ((have_the_player) && (interrupting_current_session == 0)) { - debug(1, "Connection %d: Got player lock.", conn->connection_number); + if ((have_the_player == 1) && (interrupting_current_session == 0)) { + debug(2, "Connection %d: Got player lock.", conn->connection_number); response = 0; } return response;