From bbe7526414cb1b5b3d3ff8be7b369c22e1eea026 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sun, 7 Mar 2021 23:01:50 +0100 Subject: [PATCH] [airplay] Update pairing modules (pair_ap 0.2) --- src/Makefile.am | 4 +- src/outputs/airplay.c | 20 +- src/outputs/raop.c | 6 +- src/{outputs => pair_ap}/pair-internal.h | 93 +- src/pair_ap/pair-tlv.c | 218 ++++ src/pair_ap/pair-tlv.h | 77 ++ src/{outputs => pair_ap}/pair.c | 173 +-- src/{outputs => pair_ap}/pair.h | 29 +- src/{outputs => pair_ap}/pair_fruit.c | 188 ++-- src/{outputs => pair_ap}/pair_homekit.c | 1222 ++++++++++++++-------- 10 files changed, 1466 insertions(+), 564 deletions(-) rename src/{outputs => pair_ap}/pair-internal.h (81%) create mode 100644 src/pair_ap/pair-tlv.c create mode 100644 src/pair_ap/pair-tlv.h rename src/{outputs => pair_ap}/pair.c (82%) rename src/{outputs => pair_ap}/pair.h (83%) rename src/{outputs => pair_ap}/pair_fruit.c (80%) rename src/{outputs => pair_ap}/pair_homekit.c (58%) diff --git a/src/Makefile.am b/src/Makefile.am index cfc81cac..833854fa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,9 @@ if COND_MPD MPD_SRC=mpd.c mpd.h endif -PAIR_AP_SRC=outputs/pair_fruit.c outputs/pair_homekit.c outputs/pair.c outputs/pair-internal.h outputs/pair.h +PAIR_AP_SRC = \ + pair_ap/pair_fruit.c pair_ap/pair_homekit.c pair_ap/pair-tlv.c \ + pair_ap/pair.c pair_ap/pair-tlv.h pair_ap/pair-internal.h pair_ap/pair.h AM_CPPFLAGS += -DCONFIG_GCRYPT if COND_ALSA diff --git a/src/outputs/airplay.c b/src/outputs/airplay.c index 26869aa4..09735928 100644 --- a/src/outputs/airplay.c +++ b/src/outputs/airplay.c @@ -54,7 +54,7 @@ #include "transcode.h" #include "outputs.h" -#include "pair.h" +#include "pair_ap/pair.h" /* List of TODO's for AirPlay 2 * @@ -3025,9 +3025,9 @@ payload_make_pair_generic(int step, struct evrtsp_request *req, struct airplay_s free(body); // Required!! - if (rs->pair_type == PAIR_HOMEKIT_NORMAL) + if (rs->pair_type == PAIR_CLIENT_HOMEKIT_NORMAL) evrtsp_add_header(req->output_headers, "X-Apple-HKP", "3"); - else if (rs->pair_type == PAIR_HOMEKIT_TRANSIENT) + else if (rs->pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT) evrtsp_add_header(req->output_headers, "X-Apple-HKP", "4"); return 0; @@ -3040,7 +3040,7 @@ payload_make_pair_setup1(struct evrtsp_request *req, struct airplay_session *rs, char device_id_hex[16 + 1]; if (pin) - rs->pair_type = PAIR_HOMEKIT_NORMAL; + rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL; snprintf(device_id_hex, sizeof(device_id_hex), "%016" PRIX64, airplay_device_id); @@ -3420,7 +3420,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session // Evaluate what next sequence based on response if (rs->statusflags & AIRPLAY_FLAG_ONE_TIME_PAIRING_REQUIRED) { - rs->pair_type = PAIR_HOMEKIT_NORMAL; + rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL; if (!device->auth_key) { @@ -3438,7 +3438,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session device->auth_key = NULL; device->requires_auth = 1; - rs->pair_type = PAIR_HOMEKIT_NORMAL; + rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL; rs->state = AIRPLAY_STATE_AUTH; return AIRPLAY_SEQ_PIN_START; } @@ -3449,7 +3449,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session return AIRPLAY_SEQ_ABORT; } - rs->pair_type = PAIR_HOMEKIT_TRANSIENT; + rs->pair_type = PAIR_CLIENT_HOMEKIT_TRANSIENT; rs->state = AIRPLAY_STATE_INFO; return AIRPLAY_SEQ_PAIR_TRANSIENT; } @@ -3535,14 +3535,14 @@ response_handler_pair_setup1(struct evrtsp_request *req, struct airplay_session { struct output_device *device; - if (rs->pair_type == PAIR_HOMEKIT_TRANSIENT && req->response_code == RTSP_CONNECTION_AUTH_REQUIRED) + if (rs->pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT && req->response_code == RTSP_CONNECTION_AUTH_REQUIRED) { device = outputs_device_get(rs->device_id); if (!device) return AIRPLAY_SEQ_ABORT; device->requires_auth = 1; // FIXME might be reset by mdns announcement - rs->pair_type = PAIR_HOMEKIT_NORMAL; + rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL; return AIRPLAY_SEQ_PIN_START; } @@ -3562,7 +3562,7 @@ response_handler_pair_setup2(struct evrtsp_request *req, struct airplay_session if (seq_type != AIRPLAY_SEQ_CONTINUE) return seq_type; - if (rs->pair_type != PAIR_HOMEKIT_TRANSIENT) + if (rs->pair_type != PAIR_CLIENT_HOMEKIT_TRANSIENT) return seq_type; ret = pair_setup_result(NULL, &shared_secret, &shared_secret_len, rs->pair_setup_ctx); diff --git a/src/outputs/raop.c b/src/outputs/raop.c index 15fb6e00..696f9e75 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -64,7 +64,7 @@ #include "dmap_common.h" #include "rtp_common.h" #include "outputs.h" -#include "pair.h" +#include "pair_ap/pair.h" #define ALAC_HEADER_LEN 3 @@ -4198,7 +4198,7 @@ raop_pair_verify(struct raop_session *rs) if (!device) goto error; - CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_FRUIT, device->auth_key, NULL)); + CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_CLIENT_FRUIT, device->auth_key, NULL)); ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1); if (ret < 0) @@ -4307,7 +4307,7 @@ raop_pair_setup(struct raop_session *rs, const char *pin) { int ret; - rs->pair_setup_ctx = pair_setup_new(PAIR_FRUIT, pin, NULL); + rs->pair_setup_ctx = pair_setup_new(PAIR_CLIENT_FRUIT, pin, NULL); if (!rs->pair_setup_ctx) { DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n"); diff --git a/src/outputs/pair-internal.h b/src/pair_ap/pair-internal.h similarity index 81% rename from src/outputs/pair-internal.h rename to src/pair_ap/pair-internal.h index d86983e2..1d510426 100644 --- a/src/outputs/pair-internal.h +++ b/src/pair_ap/pair-internal.h @@ -2,11 +2,10 @@ #include struct SRPUser; +struct SRPVerifier; -struct pair_setup_context +struct pair_client_setup_context { - struct pair_definition *type; - struct SRPUser *user; char pin[4]; @@ -28,23 +27,62 @@ struct pair_setup_context uint64_t salt_len; uint8_t public_key[crypto_sign_PUBLICKEYBYTES]; uint8_t private_key[crypto_sign_SECRETKEYBYTES]; - // Hex-formatet concatenation of public + private, 0-terminated - char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1]; // We don't actually use the server's epk and authtag for anything uint8_t *epk; uint64_t epk_len; uint8_t *authtag; uint64_t authtag_len; - - int setup_is_completed; - const char *errmsg; }; -struct pair_verify_context +struct pair_server_setup_context +{ + struct SRPVerifier *verifier; + + char pin[4]; + char device_id[17]; // Incl. zero term + + uint8_t *pkA; + uint64_t pkA_len; + + uint8_t *pkB; + int pkB_len; + + uint8_t *b; + int b_len; + + uint8_t *M1; + uint64_t M1_len; + + const uint8_t *M2; + int M2_len; + + uint8_t *v; + int v_len; + + uint8_t *salt; + int salt_len; +}; + +struct pair_setup_context { struct pair_definition *type; + int setup_is_completed; + const char *errmsg; + + // Hex-formatet concatenation of public + private, 0-terminated + char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1]; + + union pair_setup_union + { + struct pair_client_setup_context client; + struct pair_server_setup_context server; + } sctx; +}; + +struct pair_client_verify_context +{ char device_id[17]; // Incl. zero term uint8_t server_eph_public_key[32]; @@ -57,9 +95,19 @@ struct pair_verify_context uint8_t client_eph_private_key[32]; uint8_t shared_secret[32]; +}; + +struct pair_verify_context +{ + struct pair_definition *type; int verify_is_completed; const char *errmsg; + + union pair_verify_union + { + struct pair_client_verify_context client; + } vctx; }; struct pair_cipher_context @@ -81,7 +129,7 @@ struct pair_cipher_context struct pair_definition { - struct pair_setup_context *(*pair_setup_new)(struct pair_definition *type, const char *pin, const char *device_id); + int (*pair_setup_new)(struct pair_setup_context *sctx, const char *pin, const char *device_id); void (*pair_setup_free)(struct pair_setup_context *sctx); int (*pair_setup_result)(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx); @@ -93,6 +141,10 @@ struct pair_definition int (*pair_setup_response2)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len); int (*pair_setup_response3)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len); + int (*pair_verify_new)(struct pair_verify_context *vctx, const char *hexkey, const char *device_id); + void (*pair_verify_free)(struct pair_verify_context *vctx); + int (*pair_verify_result)(const uint8_t **key, size_t *key_len, struct pair_verify_context *vctx); + uint8_t *(*pair_verify_request1)(size_t *len, struct pair_verify_context *vctx); uint8_t *(*pair_verify_request2)(size_t *len, struct pair_verify_context *vctx); @@ -104,6 +156,8 @@ struct pair_definition ssize_t (*pair_encrypt)(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx); ssize_t (*pair_decrypt)(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx); + + int (*pair_state_get)(const char **errmsg, const uint8_t *data, size_t data_len); }; @@ -133,11 +187,16 @@ struct pair_definition #define bnum_add(bn, a, b) gcry_mpi_add(bn, a, b) #define bnum_sub(bn, a, b) gcry_mpi_sub(bn, a, b) #define bnum_mul(bn, a, b) gcry_mpi_mul(bn, a, b) +#define bnum_mod(bn, a, b) gcry_mpi_mod(bn, a, b) typedef gcry_mpi_t bnum; __attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p) { gcry_mpi_powm(bn, y, q, p); } +__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m) +{ + gcry_mpi_addm(bn, a, b, m); +} #elif CONFIG_OPENSSL #include #include @@ -162,6 +221,13 @@ __attribute__((unused)) static void bnum_mul(bnum bn, bnum a, bnum b) BN_mul(bn, a, b, ctx); BN_CTX_free(ctx); } +__attribute__((unused)) static void bnum_mod(bnum bn, bnum a, bnum b) +{ + // No error handling + BN_CTX *ctx = BN_CTX_new(); + BN_mod(bn, a, b, ctx); + BN_CTX_free(ctx); +} __attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p) { // No error handling @@ -169,6 +235,13 @@ __attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p) BN_mod_exp(bn, y, q, p, ctx); BN_CTX_free(ctx); } +__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m) +{ + // No error handling + BN_CTX *ctx = BN_CTX_new(); + BN_mod_add(bn, a, b, m, ctx); + BN_CTX_free(ctx); +} #endif diff --git a/src/pair_ap/pair-tlv.c b/src/pair_ap/pair-tlv.c new file mode 100644 index 00000000..ea1a7614 --- /dev/null +++ b/src/pair_ap/pair-tlv.c @@ -0,0 +1,218 @@ +/* + * TLV helpers are adapted from ESP homekit: + * + * + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include + +#include "pair-tlv.h" +#include "pair-internal.h" + + +static int +tlv_add_value_(pair_tlv_values_t *values, uint8_t type, uint8_t *value, size_t size) { + pair_tlv_t *tlv = malloc(sizeof(pair_tlv_t)); + if (!tlv) { + return PAIR_TLV_ERROR_MEMORY; + } + tlv->type = type; + tlv->size = size; + tlv->value = value; + tlv->next = NULL; + + if (!values->head) { + values->head = tlv; + } else { + pair_tlv_t *t = values->head; + while (t->next) { + t = t->next; + } + t->next = tlv; + } + + return 0; +} + + +pair_tlv_values_t * +pair_tlv_new() { + pair_tlv_values_t *values = malloc(sizeof(pair_tlv_values_t)); + if (!values) + return NULL; + + values->head = NULL; + return values; +} + +void +pair_tlv_free(pair_tlv_values_t *values) { + pair_tlv_t *t = values->head; + while (t) { + pair_tlv_t *t2 = t; + t = t->next; + if (t2->value) + free(t2->value); + free(t2); + } + free(values); +} + +int +pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size) { + uint8_t *data = NULL; + int ret; + if (size) { + data = malloc(size); + if (!data) { + return PAIR_TLV_ERROR_MEMORY; + } + memcpy(data, value, size); + } + ret = tlv_add_value_(values, type, data, size); + if (ret < 0) + free(data); + return ret; +} + +pair_tlv_t * +pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type) { + pair_tlv_t *t = values->head; + while (t) { + if (t->type == type) + return t; + t = t->next; + } + return NULL; +} + +int +pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size) { + size_t required_size = 0; + pair_tlv_t *t = values->head; + while (t) { + required_size += t->size + 2 * ((t->size + 254) / 255); + t = t->next; + } + + if (*size < required_size) { + *size = required_size; + return PAIR_TLV_ERROR_INSUFFICIENT_SIZE; + } + + *size = required_size; + + t = values->head; + while (t) { + uint8_t *data = t->value; + if (!t->size) { + buffer[0] = t->type; + buffer[1] = 0; + buffer += 2; + t = t->next; + continue; + } + + size_t remaining = t->size; + + while (remaining) { + buffer[0] = t->type; + size_t chunk_size = (remaining > 255) ? 255 : remaining; + buffer[1] = chunk_size; + memcpy(&buffer[2], data, chunk_size); + remaining -= chunk_size; + buffer += chunk_size + 2; + data += chunk_size; + } + + t = t->next; + } + + return 0; +} + +int +pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values) { + size_t i = 0; + int ret; + while (i < length) { + uint8_t type = buffer[i]; + size_t size = 0; + uint8_t *data = NULL; + + // scan TLVs to accumulate total size of subsequent TLVs with same type (chunked data) + size_t j = i; + while (j < length && buffer[j] == type && buffer[j+1] == 255) { + size_t chunk_size = buffer[j+1]; + size += chunk_size; + j += chunk_size + 2; + } + if (j < length && buffer[j] == type) { + size_t chunk_size = buffer[j+1]; + size += chunk_size; + } + + // allocate memory to hold all pieces of chunked data and copy data there + if (size != 0) { + data = malloc(size); + if (!data) + return PAIR_TLV_ERROR_MEMORY; + + uint8_t *p = data; + + size_t remaining = size; + while (remaining) { + size_t chunk_size = buffer[i+1]; + memcpy(p, &buffer[i+2], chunk_size); + p += chunk_size; + i += chunk_size + 2; + remaining -= chunk_size; + } + } + + ret = tlv_add_value_(values, type, data, size); + if (ret < 0) { + free(data); + return ret; + } + } + + return 0; +} + +#ifdef DEBUG_PAIR +void +pair_tlv_debug(const pair_tlv_values_t *values) +{ + printf("Received TLV values\n"); + for (pair_tlv_t *t=values->head; t; t=t->next) + { + printf("Type %d value (%zu bytes): \n", t->type, t->size); + hexdump("", t->value, t->size); + } +} +#endif + diff --git a/src/pair_ap/pair-tlv.h b/src/pair_ap/pair-tlv.h new file mode 100644 index 00000000..6ef8db20 --- /dev/null +++ b/src/pair_ap/pair-tlv.h @@ -0,0 +1,77 @@ +#ifndef __PAIR_AP_TLV_H__ +#define __PAIR_AP_TLV_H__ + +#include + +#define PAIR_TLV_ERROR_MEMORY -1 +#define PAIR_TLV_ERROR_INSUFFICIENT_SIZE -2 + +typedef enum { + TLVType_Method = 0, // (integer) Method to use for pairing. See PairMethod + TLVType_Identifier = 1, // (UTF-8) Identifier for authentication + TLVType_Salt = 2, // (bytes) 16+ bytes of random salt + TLVType_PublicKey = 3, // (bytes) Curve25519, SRP public key or signed Ed25519 key + TLVType_Proof = 4, // (bytes) Ed25519 or SRP proof + TLVType_EncryptedData = 5, // (bytes) Encrypted data with auth tag at end + TLVType_State = 6, // (integer) State of the pairing process. 1=M1, 2=M2, etc. + TLVType_Error = 7, // (integer) Error code. Must only be present if error code is + // not 0. See TLVError + TLVType_RetryDelay = 8, // (integer) Seconds to delay until retrying a setup code + TLVType_Certificate = 9, // (bytes) X.509 Certificate + TLVType_Signature = 10, // (bytes) Ed25519 + TLVType_Permissions = 11, // (integer) Bit value describing permissions of the controller + // being added. + // None (0x00): Regular user + // Bit 1 (0x01): Admin that is able to add and remove + // pairings against the accessory + TLVType_FragmentData = 13, // (bytes) Non-last fragment of data. If length is 0, + // it's an ACK. + TLVType_FragmentLast = 14, // (bytes) Last fragment of data + TLVType_Flags = 19, // Added from airplay2_receiver + TLVType_Separator = 0xff, +} TLVType; + + +typedef enum { + TLVError_Unknown = 1, // Generic error to handle unexpected errors + TLVError_Authentication = 2, // Setup code or signature verification failed + TLVError_Backoff = 3, // Client must look at the retry delay TLV item and + // wait that many seconds before retrying + TLVError_MaxPeers = 4, // Server cannot accept any more pairings + TLVError_MaxTries = 5, // Server reached its maximum number of + // authentication attempts + TLVError_Unavailable = 6, // Server pairing method is unavailable + TLVError_Busy = 7, // Server is busy and cannot accept a pairing + // request at this time +} TLVError; + +typedef struct _tlv { + struct _tlv *next; + uint8_t type; + uint8_t *value; + size_t size; +} pair_tlv_t; + + +typedef struct { + pair_tlv_t *head; +} pair_tlv_values_t; + + +pair_tlv_values_t *pair_tlv_new(); + +void pair_tlv_free(pair_tlv_values_t *values); + +int pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size); + +pair_tlv_t *pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type); + +int pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size); + +int pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values); + +#ifdef DEBUG_PAIR +void pair_tlv_debug(const pair_tlv_values_t *values); +#endif + +#endif /* !__PAIR_AP_TLV_H__ */ diff --git a/src/outputs/pair.c b/src/pair_ap/pair.c similarity index 82% rename from src/outputs/pair.c rename to src/pair_ap/pair.c index c0cd7ae1..4ebbd409 100644 --- a/src/outputs/pair.c +++ b/src/pair_ap/pair.c @@ -31,15 +31,17 @@ #include "pair.h" #include "pair-internal.h" -extern struct pair_definition pair_fruit; -extern struct pair_definition pair_homekit_normal; -extern struct pair_definition pair_homekit_transient; +extern struct pair_definition pair_client_fruit; +extern struct pair_definition pair_client_homekit_normal; +extern struct pair_definition pair_client_homekit_transient; +extern struct pair_definition pair_server_homekit_transient; // Must be in sync with enum pair_type static struct pair_definition *pair[] = { - &pair_fruit, - &pair_homekit_normal, - &pair_homekit_transient, + &pair_client_fruit, + &pair_client_homekit_normal, + &pair_client_homekit_transient, + &pair_server_homekit_transient, }; /* -------------------------- SHARED HASHING HELPERS ------------------------ */ @@ -274,10 +276,24 @@ hexdump(const char *msg, uint8_t *mem, size_t len) struct pair_setup_context * pair_setup_new(enum pair_type type, const char *pin, const char *device_id) { + struct pair_setup_context *sctx; + if (!pair[type]->pair_setup_new) return NULL; - return pair[type]->pair_setup_new(pair[type], pin, device_id); + sctx = calloc(1, sizeof(struct pair_setup_context)); + if (!sctx) + return NULL; + + sctx->type = pair[type]; + + if (pair[type]->pair_setup_new(sctx, pin, device_id) < 0) + { + free(sctx); + return NULL; + } + + return sctx; } void @@ -286,10 +302,10 @@ pair_setup_free(struct pair_setup_context *sctx) if (!sctx) return; - if (!sctx->type->pair_setup_free) - return; + if (sctx->type->pair_setup_free) + sctx->type->pair_setup_free(sctx); - return sctx->type->pair_setup_free(sctx); + free(sctx); } const char * @@ -302,12 +318,10 @@ uint8_t * pair_setup_request1(size_t *len, struct pair_setup_context *sctx) { if (!sctx->type->pair_setup_request1) - return NULL; - - return sctx->type->pair_setup_request1(len, sctx); - - if (!sctx->type->pair_setup_request1) - return NULL; + { + sctx->errmsg = "Setup request 1: Unsupported"; + return NULL; + } return sctx->type->pair_setup_request1(len, sctx); } @@ -316,7 +330,10 @@ uint8_t * pair_setup_request2(size_t *len, struct pair_setup_context *sctx) { if (!sctx->type->pair_setup_request2) - return NULL; + { + sctx->errmsg = "Setup request 2: Unsupported"; + return NULL; + } return sctx->type->pair_setup_request2(len, sctx); } @@ -325,7 +342,10 @@ uint8_t * pair_setup_request3(size_t *len, struct pair_setup_context *sctx) { if (!sctx->type->pair_setup_request3) - return NULL; + { + sctx->errmsg = "Setup request 3: Unsupported"; + return NULL; + } return sctx->type->pair_setup_request3(len, sctx); } @@ -334,7 +354,10 @@ int pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) { if (!sctx->type->pair_setup_response1) - return -1; + { + sctx->errmsg = "Setup response 1: Unsupported"; + return -1; + } return sctx->type->pair_setup_response1(sctx, data, data_len); } @@ -343,7 +366,10 @@ int pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) { if (!sctx->type->pair_setup_response2) - return -1; + { + sctx->errmsg = "Setup response 2: Unsupported"; + return -1; + } return sctx->type->pair_setup_response2(sctx, data, data_len); } @@ -352,7 +378,10 @@ int pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) { if (!sctx->type->pair_setup_response3) - return -1; + { + sctx->errmsg = "Setup response 3: Unsupported"; + return -1; + } if (sctx->type->pair_setup_response3(sctx, data, data_len) != 0) return -1; @@ -375,13 +404,21 @@ pair_setup_result(const char **hexkey, const uint8_t **key, size_t *key_len, str } if (!sctx->type->pair_setup_result) - return -1; + { + sctx->errmsg = "Setup result: Unsupported"; + return -1; + } if (sctx->type->pair_setup_result(&out_key, &out_len, sctx) != 0) - return -1; + { + return -1; + } if (2 * out_len + 1 > sizeof(sctx->auth_key)) - return -1; + { + sctx->errmsg = "Setup result: Invalid key length"; + return -1; + } ptr = sctx->auth_key; for (i = 0; i < out_len; i++) @@ -403,23 +440,8 @@ struct pair_verify_context * pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id) { struct pair_verify_context *vctx; - char hex[] = { 0, 0, 0 }; - size_t hexkey_len; - const char *ptr; - int i; - if (sodium_init() == -1) - return NULL; - - if (!hexkey) - return NULL; - - hexkey_len = strlen(hexkey); - - if (hexkey_len != 2 * sizeof(vctx->client_private_key)) - return NULL; - - if (device_id && strlen(device_id) != 16) + if (!pair[type]->pair_verify_new) return NULL; vctx = calloc(1, sizeof(struct pair_verify_context)); @@ -428,23 +450,10 @@ pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id) vctx->type = pair[type]; - if (device_id) - memcpy(vctx->device_id, device_id, strlen(device_id)); - - ptr = hexkey; - for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2) + if (pair[type]->pair_verify_new(vctx, hexkey, device_id) < 0) { - hex[0] = ptr[0]; - hex[1] = ptr[1]; - vctx->client_private_key[i] = strtol(hex, NULL, 16); - } - - ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key); - for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2) - { - hex[0] = ptr[0]; - hex[1] = ptr[1]; - vctx->client_public_key[i] = strtol(hex, NULL, 16); + free(vctx); + return NULL; } return vctx; @@ -456,6 +465,9 @@ pair_verify_free(struct pair_verify_context *vctx) if (!vctx) return; + if (vctx->type->pair_verify_free) + vctx->type->pair_verify_free(vctx); + free(vctx); } @@ -469,7 +481,10 @@ uint8_t * pair_verify_request1(size_t *len, struct pair_verify_context *vctx) { if (!vctx->type->pair_verify_request1) - return NULL; + { + vctx->errmsg = "Verify request 1: Unsupported"; + return NULL; + } return vctx->type->pair_verify_request1(len, vctx); } @@ -478,7 +493,10 @@ uint8_t * pair_verify_request2(size_t *len, struct pair_verify_context *vctx) { if (!vctx->type->pair_verify_request2) - return NULL; + { + vctx->errmsg = "Verify request 2: Unsupported"; + return NULL; + } return vctx->type->pair_verify_request2(len, vctx); } @@ -487,7 +505,10 @@ int pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) { if (!vctx->type->pair_verify_response1) - return -1; + { + vctx->errmsg = "Verify response 1: Unsupported"; + return -1; + } return vctx->type->pair_verify_response1(vctx, data, data_len); } @@ -496,7 +517,10 @@ int pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) { if (!vctx->type->pair_verify_response2) - return -1; + { + vctx->errmsg = "Verify response 2: Unsupported"; + return -1; + } if (vctx->type->pair_verify_response2(vctx, data, data_len) != 0) return -1; @@ -514,8 +538,11 @@ pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, str return -1; } - *shared_secret = vctx->shared_secret; - *shared_secret_len = sizeof(vctx->shared_secret); + if (!vctx->type->pair_verify_result) + return -1; + + if (vctx->type->pair_verify_result(shared_secret, shared_secret_len, vctx) != 0) + return -1; return 0; } @@ -551,7 +578,10 @@ ssize_t pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx) { if (!cctx->type->pair_encrypt) - return 0; + { + cctx->errmsg = "Encryption unsupported"; + return -1; + } return cctx->type->pair_encrypt(ciphertext, ciphertext_len, plaintext, plaintext_len, cctx); } @@ -560,7 +590,10 @@ ssize_t pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx) { if (!cctx->type->pair_decrypt) - return 0; + { + cctx->errmsg = "Decryption unsupported"; + return -1; + } return cctx->type->pair_decrypt(plaintext, plaintext_len, ciphertext, ciphertext_len, cctx); } @@ -576,3 +609,15 @@ pair_decrypt_rollback(struct pair_cipher_context *cctx) { cctx->decryption_counter = cctx->decryption_counter_prev; } + +int +pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len) +{ + if (!pair[type]->pair_state_get) + { + *errmsg = "Getting pair state unsupported"; + return -1; + } + + return pair[type]->pair_state_get(errmsg, data, data_len); +} diff --git a/src/outputs/pair.h b/src/pair_ap/pair.h similarity index 83% rename from src/outputs/pair.h rename to src/pair_ap/pair.h index 95e09453..b7b0f7a0 100644 --- a/src/outputs/pair.h +++ b/src/pair_ap/pair.h @@ -3,26 +3,37 @@ #include +#define PAIR_AP_VERSION_MAJOR 0 +#define PAIR_AP_VERSION_MINOR 2 + enum pair_type { // This is the pairing type required for Apple TV device verification, which // became mandatory with tvOS 10.2. - PAIR_FRUIT, + PAIR_CLIENT_FRUIT, // This is the Homekit type required for AirPlay 2 with both PIN setup and // verification - PAIR_HOMEKIT_NORMAL, + PAIR_CLIENT_HOMEKIT_NORMAL, // Same as normal except PIN is fixed to 3939 and stops after setup step 2, - // when session key is established - PAIR_HOMEKIT_TRANSIENT, + // when session key is established. This is the only mode where the server + // side is also supported. + PAIR_CLIENT_HOMEKIT_TRANSIENT, + PAIR_SERVER_HOMEKIT_TRANSIENT, }; struct pair_setup_context; struct pair_verify_context; struct pair_cipher_context; -/* When you have the pin-code (must be 4 bytes), create a new context with this +/* Client + * When you have the pin-code (must be 4 bytes), create a new context with this * function and then call pair_setup_request1(). device_id is only * required for homekit pairing, where it should have length 16. + * + * Server + * Create a new context with the pin-code to verify with, then when the request + * is received use pair_setup_response1() to read it, and then reply with using + * pair_setup_request1(). */ struct pair_setup_context * pair_setup_new(enum pair_type type, const char *pin, const char *device_id); @@ -34,6 +45,7 @@ pair_setup_free(struct pair_setup_context *sctx); const char * pair_setup_errmsg(struct pair_setup_context *sctx); + uint8_t * pair_setup_request1(size_t *len, struct pair_setup_context *sctx); uint8_t * @@ -88,6 +100,7 @@ pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, siz int pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, struct pair_verify_context *vctx); + /* When you have completed the verification you can extract a key with * pair_verify_result(). Give the shared secret as input to this function to * create a ciphering context. @@ -123,4 +136,10 @@ pair_encrypt_rollback(struct pair_cipher_context *cctx); void pair_decrypt_rollback(struct pair_cipher_context *cctx); +/* For parsing an incoming message to see what type ("state") it is. Mostly + * useful for servers. Returns 1-6 for pair-setup and 1-4 for pair-verify. + */ +int +pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len); + #endif /* !__PAIR_AP_H__ */ diff --git a/src/outputs/pair_fruit.c b/src/pair_ap/pair_fruit.c similarity index 80% rename from src/outputs/pair_fruit.c rename to src/pair_ap/pair_fruit.c index 1a2f1d5a..c718e55e 100644 --- a/src/outputs/pair_fruit.c +++ b/src/pair_ap/pair_fruit.c @@ -594,33 +594,26 @@ encrypt_ctr(unsigned char *ciphertext, int ciphertext_len, /* -------------------------- IMPLEMENTATION -------------------------------- */ -static struct pair_setup_context * -pair_setup_new(struct pair_definition *type, const char *pin, const char *device_id) +static int +pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id) { - struct pair_setup_context *sctx; + struct pair_client_setup_context *sctx = &handle->sctx.client; if (sodium_init() == -1) - return NULL; + return -1; if (!pin || strlen(pin) < 4) - return NULL; - - sctx = calloc(1, sizeof(struct pair_setup_context)); - if (!sctx) - return NULL; - - sctx->type = type; + return -1; memcpy(sctx->pin, pin, sizeof(sctx->pin)); - return sctx; + return 0; } static void -pair_setup_free(struct pair_setup_context *sctx) +pair_client_setup_free(struct pair_setup_context *handle) { - if (!sctx) - return; + struct pair_client_setup_context *sctx = &handle->sctx.client; srp_user_free(sctx->user); @@ -629,13 +622,12 @@ pair_setup_free(struct pair_setup_context *sctx) free(sctx->salt); free(sctx->epk); free(sctx->authtag); - - free(sctx); } static uint8_t * -pair_setup_request1(size_t *len, struct pair_setup_context *sctx) +pair_client_setup_request1(size_t *len, struct pair_setup_context *handle) { + struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; plist_t method; plist_t user; @@ -659,8 +651,9 @@ pair_setup_request1(size_t *len, struct pair_setup_context *sctx) } static uint8_t * -pair_setup_request2(size_t *len, struct pair_setup_context *sctx) +pair_client_setup_request2(size_t *len, struct pair_setup_context *handle) { + struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; plist_t pk; plist_t proof; @@ -688,8 +681,9 @@ pair_setup_request2(size_t *len, struct pair_setup_context *sctx) } static uint8_t * -pair_setup_request3(size_t *len, struct pair_setup_context *sctx) +pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) { + struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; plist_t epk; plist_t authtag; @@ -707,21 +701,21 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) session_key = srp_user_get_session_key(sctx->user, &session_key_len); if (!session_key) { - sctx->errmsg = "Setup request 3: No valid session key"; + handle->errmsg = "Setup request 3: No valid session key"; return NULL; } ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_SETUP_KEY, strlen(AES_SETUP_KEY), session_key, session_key_len); if (ret < 0) { - sctx->errmsg = "Setup request 3: Hashing of key string and shared secret failed"; + handle->errmsg = "Setup request 3: Hashing of key string and shared secret failed"; return NULL; } ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_SETUP_IV, strlen(AES_SETUP_IV), session_key, session_key_len); if (ret < 0) { - sctx->errmsg = "Setup request 3: Hashing of iv string and shared secret failed"; + handle->errmsg = "Setup request 3: Hashing of iv string and shared secret failed"; return NULL; } @@ -735,7 +729,7 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) ret = encrypt_gcm(encrypted, sizeof(encrypted), tag, sctx->public_key, sizeof(sctx->public_key), key, iv, &errmsg); if (ret < 0) { - sctx->errmsg = errmsg; + handle->errmsg = errmsg; return NULL; } @@ -753,8 +747,9 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) } static int -pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) { + struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; plist_t pk; plist_t salt; @@ -765,7 +760,7 @@ pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_ salt = plist_dict_get_item(dict, "salt"); if (!pk || !salt) { - sctx->errmsg = "Setup response 1: Missing pk or salt"; + handle->errmsg = "Setup response 1: Missing pk or salt"; plist_free(dict); return -1; } @@ -779,8 +774,9 @@ pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_ } static int -pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) { + struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; plist_t proof; @@ -789,7 +785,7 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_ proof = plist_dict_get_item(dict, "proof"); if (!proof) { - sctx->errmsg = "Setup response 2: Missing proof"; + handle->errmsg = "Setup response 2: Missing proof"; plist_free(dict); return -1; } @@ -802,7 +798,7 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_ srp_user_verify_session(sctx->user, (const unsigned char *)sctx->M2); if (!srp_user_is_authenticated(sctx->user)) { - sctx->errmsg = "Setup response 2: Server authentication failed"; + handle->errmsg = "Setup response 2: Server authentication failed"; return -1; } @@ -810,8 +806,9 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_ } static int -pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) { + struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; plist_t epk; plist_t authtag; @@ -821,7 +818,7 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ epk = plist_dict_get_item(dict, "epk"); if (!epk) { - sctx->errmsg = "Setup response 3: Missing epk"; + handle->errmsg = "Setup response 3: Missing epk"; plist_free(dict); return -1; } @@ -831,7 +828,7 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ authtag = plist_dict_get_item(dict, "authTag"); if (!authtag) { - sctx->errmsg = "Setup response 3: Missing authTag"; + handle->errmsg = "Setup response 3: Missing authTag"; plist_free(dict); return -1; } @@ -840,17 +837,19 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ plist_free(dict); - sctx->setup_is_completed = 1; + handle->setup_is_completed = 1; return 0; } static int -pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx) +pair_client_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *handle) { + struct pair_client_setup_context *sctx = &handle->sctx.client; + // Last 32 bytes of private key should match public key, but check assumption if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0) { - sctx->errmsg = "Pair setup result: Unexpected keys, private key does not match public key"; + handle->errmsg = "Pair setup result: Unexpected keys, private key does not match public key"; return -1; } @@ -860,9 +859,55 @@ pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_contex } -static uint8_t * -pair_verify_request1(size_t *len, struct pair_verify_context *vctx) +static int +pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, const char *device_id) { + struct pair_client_verify_context *vctx = &handle->vctx.client; + char hex[] = { 0, 0, 0 }; + size_t hexkey_len; + const char *ptr; + int i; + + if (sodium_init() == -1) + return -1; + + if (!hexkey) + return -1; + + hexkey_len = strlen(hexkey); + + if (hexkey_len != 2 * sizeof(vctx->client_private_key)) + return -1; + + if (device_id && strlen(device_id) != 16) + return -1; + + if (device_id) + memcpy(vctx->device_id, device_id, strlen(device_id)); + + ptr = hexkey; + for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2) + { + hex[0] = ptr[0]; + hex[1] = ptr[1]; + vctx->client_private_key[i] = strtol(hex, NULL, 16); + } + + ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key); + for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2) + { + hex[0] = ptr[0]; + hex[1] = ptr[1]; + vctx->client_public_key[i] = strtol(hex, NULL, 16); + } + + return 0; +} + +static uint8_t * +pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) +{ + struct pair_client_verify_context *vctx = &handle->vctx.client; const uint8_t basepoint[32] = {9}; uint8_t *data; int ret; @@ -870,7 +915,7 @@ pair_verify_request1(size_t *len, struct pair_verify_context *vctx) ret = crypto_scalarmult(vctx->client_eph_public_key, vctx->client_eph_private_key, basepoint); if (ret < 0) { - vctx->errmsg = "Verify request 1: Curve 25519 returned an error"; + handle->errmsg = "Verify request 1: Curve 25519 returned an error"; return NULL; } @@ -878,7 +923,7 @@ pair_verify_request1(size_t *len, struct pair_verify_context *vctx) data = calloc(1, *len); if (!data) { - vctx->errmsg = "Verify request 1: Out of memory"; + handle->errmsg = "Verify request 1: Out of memory"; return NULL; } @@ -890,8 +935,9 @@ pair_verify_request1(size_t *len, struct pair_verify_context *vctx) } static uint8_t * -pair_verify_request2(size_t *len, struct pair_verify_context *vctx) +pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) { + struct pair_client_verify_context *vctx = &handle->vctx.client; uint8_t shared_secret[crypto_scalarmult_BYTES]; uint8_t key[SHA512_DIGEST_LENGTH]; uint8_t iv[SHA512_DIGEST_LENGTH]; @@ -905,7 +951,7 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) data = calloc(1, *len); if (!data) { - vctx->errmsg = "Verify request 2: Out of memory"; + handle->errmsg = "Verify request 2: Out of memory"; return NULL; } @@ -919,28 +965,28 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) ret = crypto_scalarmult(shared_secret, vctx->client_eph_private_key, vctx->server_eph_public_key); if (ret < 0) { - vctx->errmsg = "Verify request 2: Curve 25519 returned an error"; + handle->errmsg = "Verify request 2: Curve 25519 returned an error"; return NULL; } ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_VERIFY_KEY, strlen(AES_VERIFY_KEY), shared_secret, sizeof(shared_secret)); if (ret < 0) { - vctx->errmsg = "Verify request 2: Hashing of key string and shared secret failed"; + handle->errmsg = "Verify request 2: Hashing of key string and shared secret failed"; return NULL; } ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_VERIFY_IV, strlen(AES_VERIFY_IV), shared_secret, sizeof(shared_secret)); if (ret < 0) { - vctx->errmsg = "Verify request 2: Hashing of iv string and shared secret failed"; + handle->errmsg = "Verify request 2: Hashing of iv string and shared secret failed"; return NULL; } ret = encrypt_ctr(encrypted, sizeof(encrypted), vctx->server_public_key, sizeof(vctx->server_public_key), signature, sizeof(signature), key, iv, &errmsg); if (ret < 0) { - vctx->errmsg = errmsg; + handle->errmsg = errmsg; return NULL; } @@ -948,7 +994,7 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) data = calloc(1, *len); if (!data) { - vctx->errmsg = "Verify request 2: Out of memory"; + handle->errmsg = "Verify request 2: Out of memory"; return NULL; } @@ -958,14 +1004,15 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) } static int -pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) +pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) { + struct pair_client_verify_context *vctx = &handle->vctx.client; size_t wanted; wanted = sizeof(vctx->server_eph_public_key) + sizeof(vctx->server_public_key); if (data_len < wanted) { - vctx->errmsg = "Verify response 2: Unexpected response (too short)"; + handle->errmsg = "Verify response 2: Unexpected response (too short)"; return -1; } @@ -976,29 +1023,44 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz } static int -pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) +pair_client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) { // TODO actually check response return 0; } -struct pair_definition pair_fruit = +static int +pair_client_verify_result(const uint8_t **key, size_t *key_len, struct pair_verify_context *handle) { - .pair_setup_new = pair_setup_new, - .pair_setup_free = pair_setup_free, - .pair_setup_result = pair_setup_result, + struct pair_client_verify_context *vctx = &handle->vctx.client; - .pair_setup_request1 = pair_setup_request1, - .pair_setup_request2 = pair_setup_request2, - .pair_setup_request3 = pair_setup_request3, + *key = vctx->shared_secret; + *key_len = sizeof(vctx->shared_secret); - .pair_setup_response1 = pair_setup_response1, - .pair_setup_response2 = pair_setup_response2, - .pair_setup_response3 = pair_setup_response3, + return 0; +} - .pair_verify_request1 = pair_verify_request1, - .pair_verify_request2 = pair_verify_request2, - .pair_verify_response1 = pair_verify_response1, - .pair_verify_response2 = pair_verify_response2, +struct pair_definition pair_client_fruit = +{ + .pair_setup_new = pair_client_setup_new, + .pair_setup_free = pair_client_setup_free, + .pair_setup_result = pair_client_setup_result, + + .pair_setup_request1 = pair_client_setup_request1, + .pair_setup_request2 = pair_client_setup_request2, + .pair_setup_request3 = pair_client_setup_request3, + + .pair_setup_response1 = pair_client_setup_response1, + .pair_setup_response2 = pair_client_setup_response2, + .pair_setup_response3 = pair_client_setup_response3, + + .pair_verify_new = pair_client_verify_new, + .pair_verify_result = pair_client_verify_result, + + .pair_verify_request1 = pair_client_verify_request1, + .pair_verify_request2 = pair_client_verify_request2, + + .pair_verify_response1 = pair_client_verify_response1, + .pair_verify_response2 = pair_client_verify_response2, }; diff --git a/src/outputs/pair_homekit.c b/src/pair_ap/pair_homekit.c similarity index 58% rename from src/outputs/pair_homekit.c rename to src/pair_ap/pair_homekit.c index 20a1af54..81cc352d 100644 --- a/src/outputs/pair_homekit.c +++ b/src/pair_ap/pair_homekit.c @@ -44,6 +44,7 @@ #include #include "pair-internal.h" +#include "pair-tlv.h" /* ----------------------------- DEFINES ETC ------------------------------- */ @@ -121,8 +122,9 @@ enum pair_flags { }; // Forwards -const struct pair_definition pair_homekit_normal; -const struct pair_definition pair_homekit_transient; +const struct pair_definition pair_client_homekit_normal; +const struct pair_definition pair_client_homekit_transient; +const struct pair_definition pair_server_homekit_transient; /* ---------------------------------- SRP ----------------------------------- */ @@ -162,6 +164,22 @@ struct SRPUser int session_key_len; }; +struct SRPVerifier +{ + enum hash_alg alg; + NGConstant *ng; + + const unsigned char *bytes_B; + int authenticated; + + char *username; + + unsigned char M [SHA512_DIGEST_LENGTH]; + unsigned char H_AMK [SHA512_DIGEST_LENGTH]; + unsigned char session_key [SHA512_DIGEST_LENGTH]; + int session_key_len; +}; + struct NGHex { const char *n_hex; @@ -288,6 +306,8 @@ calculate_H_AMK(enum hash_alg alg, unsigned char *dest, const bnum A, const unsi hash_final( alg, &ctx, dest ); } +/* ----------------------- SRP for the client side -------------------------- */ + static struct SRPUser * srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username, const unsigned char *bytes_password, int len_password, @@ -487,263 +507,287 @@ srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK) } -/* ---------------------------------- TLV ----------------------------------- */ +/* ----------------------- SRP for the server side -------------------------- */ -#define TLV_ERROR_MEMORY -1 -#define TLV_ERROR_INSUFFICIENT_SIZE -2 +static int +srp_create_salted_verification_key(enum hash_alg alg, + SRP_NGType ng_type, const char *username, + const unsigned char *password, int len_password, + unsigned char **bytes_s, int *len_s, + unsigned char **bytes_v, int *len_v, + const char *n_hex, const char *g_hex ) +{ + bnum s, v, x; + NGConstant *ng; -typedef enum { - TLVType_Method = 0, // (integer) Method to use for pairing. See PairMethod - TLVType_Identifier = 1, // (UTF-8) Identifier for authentication - TLVType_Salt = 2, // (bytes) 16+ bytes of random salt - TLVType_PublicKey = 3, // (bytes) Curve25519, SRP public key or signed Ed25519 key - TLVType_Proof = 4, // (bytes) Ed25519 or SRP proof - TLVType_EncryptedData = 5, // (bytes) Encrypted data with auth tag at end - TLVType_State = 6, // (integer) State of the pairing process. 1=M1, 2=M2, etc. - TLVType_Error = 7, // (integer) Error code. Must only be present if error code is - // not 0. See TLVError - TLVType_RetryDelay = 8, // (integer) Seconds to delay until retrying a setup code - TLVType_Certificate = 9, // (bytes) X.509 Certificate - TLVType_Signature = 10, // (bytes) Ed25519 - TLVType_Permissions = 11, // (integer) Bit value describing permissions of the controller - // being added. - // None (0x00): Regular user - // Bit 1 (0x01): Admin that is able to add and remove - // pairings against the accessory - TLVType_FragmentData = 13, // (bytes) Non-last fragment of data. If length is 0, - // it's an ACK. - TLVType_FragmentLast = 14, // (bytes) Last fragment of data - TLVType_Flags = 19, // Added from airplay2_receiver - TLVType_Separator = 0xff, -} TLVType; + bnum_new(s); + bnum_new(v); + x = NULL; + ng = new_ng(ng_type, n_hex, g_hex); -typedef enum { - TLVError_Unknown = 1, // Generic error to handle unexpected errors - TLVError_Authentication = 2, // Setup code or signature verification failed - TLVError_Backoff = 3, // Client must look at the retry delay TLV item and - // wait that many seconds before retrying - TLVError_MaxPeers = 4, // Server cannot accept any more pairings - TLVError_MaxTries = 5, // Server reached its maximum number of - // authentication attempts - TLVError_Unavailable = 6, // Server pairing method is unavailable - TLVError_Busy = 7, // Server is busy and cannot accept a pairing - // request at this time -} TLVError; + *bytes_s = NULL; + *bytes_v = NULL; -typedef struct _tlv { - struct _tlv *next; - uint8_t type; - uint8_t *value; - size_t size; -} tlv_t; + if (!s || !v || !ng) + goto error; + bnum_random(s, 128); // MODIFIED from csrp's BN_rand(s, 32, -1, 0) -typedef struct { - tlv_t *head; -} tlv_values_t; + x = calculate_x(alg, s, username, password, len_password); + if (!x) + goto error; + bnum_modexp(v, ng->g, x, ng->N); -static tlv_values_t * -tlv_new() { - tlv_values_t *values = malloc(sizeof(tlv_values_t)); - if (!values) - return NULL; + *len_s = bnum_num_bytes(s); + *len_v = bnum_num_bytes(v); - values->head = NULL; - return values; + *bytes_s = malloc(*len_s); + *bytes_v = malloc(*len_v); + if (!(*bytes_s) || !(*bytes_v)) + goto error; + + bnum_bn2bin(s, (unsigned char *) *bytes_s, *len_s); + bnum_bn2bin(v, (unsigned char *) *bytes_v, *len_v); + + free_ng(ng); + bnum_free(s); + bnum_free(v); + bnum_free(x); + return 0; + + error: + free(*bytes_s); + free(*bytes_v); + free_ng(ng); + bnum_free(s); + bnum_free(v); + bnum_free(x); + return -1; +} + +static int +srp_verifier_start_authentication(enum hash_alg alg, SRP_NGType ng_type, + const unsigned char *bytes_v, int len_v, + unsigned char **bytes_b, int *len_b, + unsigned char **bytes_B, int *len_B, + const char *n_hex, const char *g_hex) +{ + bnum v, k, b, B, tmp1, tmp2; + NGConstant *ng; + + v = NULL; + k = NULL; + bnum_new(b); + bnum_new(B); + bnum_new(tmp1); + bnum_new(tmp2); + + *len_b = 0; + *bytes_b = NULL; + *len_B = 0; + *bytes_B = NULL; + + ng = new_ng(ng_type, n_hex, g_hex); + + if (!b || !B || !tmp1 || !tmp2 || !ng) + goto error; + + bnum_bin2bn(v, bytes_v, len_v); + + bnum_random(b, 256); // MODIFIED from BN_rand(b, 256, -1, 0) + + k = H_nn_pad(alg, ng->N, ng->g); // MODIFIED from H_nn(alg, ng->N, ng->g) + if (!k) + goto error; + + // B = kv + g^b + bnum_mul(tmp1, k, v); + bnum_modexp(tmp2, ng->g, b, ng->N); + bnum_modadd(B, tmp1, tmp2, ng->N); + + *len_B = bnum_num_bytes(B); + *len_b = bnum_num_bytes(b); + + *bytes_B = malloc(*len_B); + *bytes_b = malloc(*len_b); + if (!(*bytes_B) || !(*bytes_b)) + goto error; + + bnum_bn2bin(B, (unsigned char *) *bytes_B, *len_B); + bnum_bn2bin(b, (unsigned char *) *bytes_b, *len_b); + + bnum_free(b); + bnum_free(B); + bnum_free(v); + bnum_free(k); + bnum_free(tmp1); + bnum_free(tmp2); + free_ng(ng); + return 0; + + error: + free(*bytes_B); + free(*bytes_b); + bnum_free(b); + bnum_free(B); + bnum_free(v); + bnum_free(k); + bnum_free(tmp1); + bnum_free(tmp2); + free_ng(ng); + return -1; } static void -tlv_free(tlv_values_t *values) { - tlv_t *t = values->head; - while (t) { - tlv_t *t2 = t; - t = t->next; - if (t2->value) - free(t2->value); - free(t2); - } - free(values); +srp_verifier_free(struct SRPVerifier *ver) +{ + if (!ver) + return; + + free_ng(ver->ng); + + free(ver->username); + + memset(ver, 0, sizeof(struct SRPVerifier)); + free(ver); } -static int -tlv_add_value_(tlv_values_t *values, uint8_t type, uint8_t *value, size_t size) { - tlv_t *tlv = malloc(sizeof(tlv_t)); - if (!tlv) { - return TLV_ERROR_MEMORY; - } - tlv->type = type; - tlv->size = size; - tlv->value = value; - tlv->next = NULL; +static struct SRPVerifier * +srp_verifier_new(enum hash_alg alg, SRP_NGType ng_type, const char *username, + const unsigned char *bytes_s, int len_s, + const unsigned char *bytes_v, int len_v, + const unsigned char *bytes_A, int len_A, + const unsigned char *bytes_b, int len_b, + const unsigned char *bytes_B, int len_B, + const char *n_hex, const char *g_hex ) +{ + struct SRPVerifier *ver = NULL; + bnum s, v, A, b, B, S, tmp1, tmp2, u, k; + NGConstant *ng; + size_t ulen; - if (!values->head) { - values->head = tlv; - } else { - tlv_t *t = values->head; - while (t->next) { - t = t->next; - } - t->next = tlv; - } + bnum_bin2bn(s, bytes_s, len_s); + bnum_bin2bn(v, bytes_v, len_v); + bnum_bin2bn(A, bytes_A, len_A); + bnum_bin2bn(b, bytes_b, len_b); + bnum_bin2bn(B, bytes_B, len_B); + bnum_new(S); + bnum_new(tmp1); + bnum_new(tmp2); + u = NULL; + k = NULL; - return 0; + ng = new_ng(ng_type, n_hex, g_hex); + + if (!s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ng) + goto error; + + ver = calloc(1, sizeof(struct SRPVerifier)); + if (!ver) + goto error; + + ulen = strlen(username) + 1; + + ver->alg = alg; + ver->ng = ng; + + ver->username = malloc(ulen); + if (!ver->username) + goto error; + + memcpy(ver->username, username, ulen); + + ver->authenticated = 0; + + // SRP-6a safety check + bnum_mod(tmp1, A, ng->N); + 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) + + // S = (A *(v^u)) ^ b + bnum_modexp(tmp1, v, u, ng->N); + bnum_mul(tmp2, A, tmp1); + bnum_modexp(S, tmp2, b, ng->N); + + hash_num(alg, S, ver->session_key); + ver->session_key_len = hash_length(ver->alg); + + calculate_M(alg, ng, ver->M, username, s, A, B, ver->session_key, ver->session_key_len); + calculate_H_AMK(alg, ver->H_AMK, A, ver->M, ver->session_key, ver->session_key_len); + + ver->bytes_B = bytes_B; + + bnum_free(s); + bnum_free(v); + bnum_free(A); + bnum_free(u); + bnum_free(k); + bnum_free(B); + bnum_free(S); + bnum_free(b); + bnum_free(tmp1); + bnum_free(tmp2); + return ver; + + error: + srp_verifier_free(ver); + bnum_free(s); + bnum_free(v); + bnum_free(A); + bnum_free(u); + bnum_free(k); + bnum_free(B); + bnum_free(S); + bnum_free(b); + bnum_free(tmp1); + bnum_free(tmp2); + return NULL; } -static int -tlv_add_value(tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size) { - uint8_t *data = NULL; - int ret; - if (size) { - data = malloc(size); - if (!data) { - return TLV_ERROR_MEMORY; - } - memcpy(data, value, size); +// user_M must be exactly SHA512_DIGEST_LENGTH bytes in size +static void +srp_verifier_verify_session(struct SRPVerifier *ver, const unsigned char *user_M, const unsigned char **bytes_HAMK) +{ + if (memcmp(ver->M, user_M, hash_length(ver->alg)) == 0) + { + ver->authenticated = 1; + *bytes_HAMK = ver->H_AMK; } - ret = tlv_add_value_(values, type, data, size); - if (ret < 0) - free(data); - return ret; + else + *bytes_HAMK = NULL; } -static tlv_t * -tlv_get_value(const tlv_values_t *values, uint8_t type) { - tlv_t *t = values->head; - while (t) { - if (t->type == type) - return t; - t = t->next; - } - return NULL; +static const unsigned char * +srp_verifier_get_session_key(struct SRPVerifier *ver, int *key_length) +{ + if (key_length) + *key_length = hash_length(ver->alg); + + return ver->session_key; } -static int -tlv_format(const tlv_values_t *values, uint8_t *buffer, size_t *size) { - size_t required_size = 0; - tlv_t *t = values->head; - while (t) { - required_size += t->size + 2 * ((t->size + 254) / 255); - t = t->next; - } - - if (*size < required_size) { - *size = required_size; - return TLV_ERROR_INSUFFICIENT_SIZE; - } - - *size = required_size; - - t = values->head; - while (t) { - uint8_t *data = t->value; - if (!t->size) { - buffer[0] = t->type; - buffer[1] = 0; - buffer += 2; - t = t->next; - continue; - } - - size_t remaining = t->size; - - while (remaining) { - buffer[0] = t->type; - size_t chunk_size = (remaining > 255) ? 255 : remaining; - buffer[1] = chunk_size; - memcpy(&buffer[2], data, chunk_size); - remaining -= chunk_size; - buffer += chunk_size + 2; - data += chunk_size; - } - - t = t->next; - } - - return 0; -} - -static int -tlv_parse(const uint8_t *buffer, size_t length, tlv_values_t *values) { - size_t i = 0; - int ret; - while (i < length) { - uint8_t type = buffer[i]; - size_t size = 0; - uint8_t *data = NULL; - - // scan TLVs to accumulate total size of subsequent TLVs with same type (chunked data) - size_t j = i; - while (j < length && buffer[j] == type && buffer[j+1] == 255) { - size_t chunk_size = buffer[j+1]; - size += chunk_size; - j += chunk_size + 2; - } - if (j < length && buffer[j] == type) { - size_t chunk_size = buffer[j+1]; - size += chunk_size; - } - - // allocate memory to hold all pieces of chunked data and copy data there - if (size != 0) { - data = malloc(size); - if (!data) - return TLV_ERROR_MEMORY; - - uint8_t *p = data; - - size_t remaining = size; - while (remaining) { - size_t chunk_size = buffer[i+1]; - memcpy(p, &buffer[i+2], chunk_size); - p += chunk_size; - i += chunk_size + 2; - remaining -= chunk_size; - } - } - - ret = tlv_add_value_(values, type, data, size); - if (ret < 0) { - free(data); - return ret; - } - } - - return 0; -} - - /* -------------------------------- HELPERS --------------------------------- */ -#ifdef DEBUG_PAIR -static void -tlv_debug(const tlv_values_t *values) +static pair_tlv_values_t * +message_process(const uint8_t *data, size_t data_len, const char **errmsg) { - printf("Received TLV values\n"); - for (tlv_t *t=values->head; t; t=t->next) - { - printf("Type %d value (%zu bytes): \n", t->type, t->size); - hexdump("", t->value, t->size); - } -} -#endif - -static tlv_values_t * -response_process(const uint8_t *data, size_t data_len, const char **errmsg) -{ - tlv_values_t *response; - tlv_t *error; + pair_tlv_values_t *response; + pair_tlv_t *error; int ret; - response = tlv_new(); + response = pair_tlv_new(); if (!response) { *errmsg = "Out of memory\n"; return NULL; } - ret = tlv_parse(data, data_len, response); + ret = pair_tlv_parse(data, data_len, response); if (ret < 0) { *errmsg = "Could not parse TLV\n"; @@ -751,10 +795,10 @@ response_process(const uint8_t *data, size_t data_len, const char **errmsg) } #ifdef DEBUG_PAIR - tlv_debug(response); + pair_tlv_debug(response); #endif - error = tlv_get_value(response, TLVType_Error); + error = pair_tlv_get_value(response, TLVType_Error); if (error) { if (error->value[0] == TLVError_Authentication) @@ -776,7 +820,7 @@ response_process(const uint8_t *data, size_t data_len, const char **errmsg) return response; error: - tlv_free(response); + pair_tlv_free(response); return NULL; } @@ -987,7 +1031,7 @@ decrypt_chacha(uint8_t *plain, uint8_t *cipher, size_t cipher_len, const uint8_t static int create_and_sign_device_info(uint8_t *data, size_t *data_len, const char *device_id, uint8_t *device_pk, size_t device_pk_len, uint8_t *pk, size_t pk_len, uint8_t *sk) { - tlv_values_t *tlv; + pair_tlv_values_t *tlv; uint8_t *device_info; uint32_t device_info_len; size_t device_id_len; @@ -1006,59 +1050,52 @@ create_and_sign_device_info(uint8_t *data, size_t *data_len, const char *device_ crypto_sign_detached(signature, NULL, device_info, device_info_len, sk); free(device_info); - tlv = tlv_new(); - tlv_add_value(tlv, TLVType_Identifier, (unsigned char *)device_id, device_id_len); - tlv_add_value(tlv, TLVType_Signature, signature, sizeof(signature)); + tlv = pair_tlv_new(); + pair_tlv_add_value(tlv, TLVType_Identifier, (unsigned char *)device_id, device_id_len); + pair_tlv_add_value(tlv, TLVType_Signature, signature, sizeof(signature)); - ret = tlv_format(tlv, data, data_len); + ret = pair_tlv_format(tlv, data, data_len); - tlv_free(tlv); + pair_tlv_free(tlv); return ret; } -/* -------------------------- IMPLEMENTATION -------------------------------- */ +/* ------------------------- CLIENT IMPLEMENTATION -------------------------- */ -static struct pair_setup_context * -pair_setup_new(struct pair_definition *type, const char *pin, const char *device_id) +static int +pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id) { - struct pair_setup_context *sctx; + struct pair_client_setup_context *sctx = &handle->sctx.client; if (sodium_init() == -1) - return NULL; + return -1; - if (type == &pair_homekit_normal) + if (handle->type == &pair_client_homekit_normal) { if (!pin || strlen(pin) < 4) - return NULL; + return -1; } - else if (type == &pair_homekit_transient && !pin) + else if (handle->type == &pair_client_homekit_transient && !pin) { pin = "3939"; } if (device_id && strlen(device_id) != 16) - return NULL; - - sctx = calloc(1, sizeof(struct pair_setup_context)); - if (!sctx) - return NULL; - - sctx->type = type; + return -1; memcpy(sctx->pin, pin, sizeof(sctx->pin)); if (device_id) memcpy(sctx->device_id, device_id, strlen(device_id)); - return sctx; + return 0; } static void -pair_setup_free(struct pair_setup_context *sctx) +pair_client_setup_free(struct pair_setup_context *handle) { - if (!sctx) - return; + struct pair_client_setup_context *sctx = &handle->sctx.client; srp_user_free(sctx->user); @@ -1067,14 +1104,13 @@ pair_setup_free(struct pair_setup_context *sctx) free(sctx->salt); free(sctx->epk); free(sctx->authtag); - - free(sctx); } static uint8_t * -pair_setup_request1(size_t *len, struct pair_setup_context *sctx) +pair_client_setup_request1(size_t *len, struct pair_setup_context *handle) { - tlv_values_t *request; + struct pair_client_setup_context *sctx = &handle->sctx.client; + pair_tlv_values_t *request; uint8_t *data; size_t data_len; uint8_t method; @@ -1084,54 +1120,55 @@ pair_setup_request1(size_t *len, struct pair_setup_context *sctx) data_len = REQUEST_BUFSIZE; data = malloc(data_len); - request = tlv_new(); + request = pair_tlv_new(); // Test here instead of setup_new() so we can give an error message if(*(char *)&endian_test != 1) { - sctx->errmsg = "Setup request 1: No support for big endian architechture"; + handle->errmsg = "Setup request 1: No support for big endian architechture"; goto error; } sctx->user = srp_user_new(HASH_SHA512, SRP_NG_3072, USERNAME, (unsigned char *)sctx->pin, sizeof(sctx->pin), 0, 0); if (!sctx->user) { - sctx->errmsg = "Setup request 1: Create SRP user failed"; + handle->errmsg = "Setup request 1: Create SRP user failed"; goto error; } method = PairingMethodPairSetup; - tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG01].state, sizeof(pair_keys_map[PAIR_SETUP_MSG01].state)); - tlv_add_value(request, TLVType_Method, &method, sizeof(method)); + pair_tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG01].state, sizeof(pair_keys_map[PAIR_SETUP_MSG01].state)); + pair_tlv_add_value(request, TLVType_Method, &method, sizeof(method)); - if (sctx->type == &pair_homekit_transient) + if (handle->type == &pair_client_homekit_transient) { flags = PairingFlagsTransient; - tlv_add_value(request, TLVType_Flags, &flags, sizeof(flags)); + pair_tlv_add_value(request, TLVType_Flags, &flags, sizeof(flags)); } - ret = tlv_format(request, data, &data_len); + ret = pair_tlv_format(request, data, &data_len); if (ret < 0) { - sctx->errmsg = "Setup request 1: tlv_format returned an error"; + handle->errmsg = "Setup request 1: pair_tlv_format returned an error"; goto error; } *len = data_len; - tlv_free(request); + pair_tlv_free(request); return data; error: - tlv_free(request); + pair_tlv_free(request); free(data); return NULL; } static uint8_t * -pair_setup_request2(size_t *len, struct pair_setup_context *sctx) +pair_client_setup_request2(size_t *len, struct pair_setup_context *handle) { - tlv_values_t *request; + struct pair_client_setup_context *sctx = &handle->sctx.client; + pair_tlv_values_t *request; uint8_t *data; size_t data_len; const char *auth_username = NULL; @@ -1139,7 +1176,7 @@ pair_setup_request2(size_t *len, struct pair_setup_context *sctx) data_len = REQUEST_BUFSIZE; data = malloc(data_len); - request = tlv_new(); + request = pair_tlv_new(); // Calculate A srp_user_start_authentication(sctx->user, &auth_username, &sctx->pkA, &sctx->pkA_len); @@ -1147,32 +1184,33 @@ pair_setup_request2(size_t *len, struct pair_setup_context *sctx) // Calculate M1 (client proof) srp_user_process_challenge(sctx->user, (const unsigned char *)sctx->salt, sctx->salt_len, (const unsigned char *)sctx->pkB, sctx->pkB_len, &sctx->M1, &sctx->M1_len); - tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG03].state, sizeof(pair_keys_map[PAIR_SETUP_MSG03].state)); - tlv_add_value(request, TLVType_PublicKey, sctx->pkA, sctx->pkA_len); - tlv_add_value(request, TLVType_Proof, sctx->M1, sctx->M1_len); + pair_tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG03].state, sizeof(pair_keys_map[PAIR_SETUP_MSG03].state)); + pair_tlv_add_value(request, TLVType_PublicKey, sctx->pkA, sctx->pkA_len); + pair_tlv_add_value(request, TLVType_Proof, sctx->M1, sctx->M1_len); - ret = tlv_format(request, data, &data_len); + ret = pair_tlv_format(request, data, &data_len); if (ret < 0) { - sctx->errmsg = "Setup request 2: tlv_format returned an error"; + handle->errmsg = "Setup request 2: pair_tlv_format returned an error"; goto error; } *len = data_len; - tlv_free(request); + pair_tlv_free(request); return data; error: - tlv_free(request); + pair_tlv_free(request); free(data); return NULL; } static uint8_t * -pair_setup_request3(size_t *len, struct pair_setup_context *sctx) +pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) { - tlv_values_t *request; + struct pair_client_setup_context *sctx = &handle->sctx.client; + pair_tlv_values_t *request; uint8_t *data; size_t data_len; const unsigned char *session_key; @@ -1181,7 +1219,7 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; uint8_t derived_key[32]; - tlv_values_t *append; + pair_tlv_values_t *append; size_t append_len; uint8_t *encrypted_data = NULL; size_t encrypted_data_len; @@ -1189,19 +1227,19 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) data_len = REQUEST_BUFSIZE; data = malloc(data_len); - request = tlv_new(); + request = pair_tlv_new(); session_key = srp_user_get_session_key(sctx->user, &session_key_len); if (!session_key) { - sctx->errmsg = "Setup request 3: No valid session key"; + handle->errmsg = "Setup request 3: No valid session key"; goto error; } ret = hkdf_extract_expand(device_x, sizeof(device_x), session_key, session_key_len, PAIR_SETUP_SIGN); if (ret < 0) { - sctx->errmsg = "Setup request 3: hkdf error getting device_x"; + handle->errmsg = "Setup request 3: hkdf error getting device_x"; goto error; } @@ -1210,26 +1248,26 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) ret = create_and_sign_device_info(data, &data_len, sctx->device_id, device_x, sizeof(device_x), sctx->public_key, sizeof(sctx->public_key), sctx->private_key); if (ret < 0) { - sctx->errmsg = "Setup request 3: error creating signed device info"; + handle->errmsg = "Setup request 3: error creating signed device info"; goto error; } ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, 64, PAIR_SETUP_MSG05); // TODO is session_key_len always 64? if (ret < 0) { - sctx->errmsg = "Setup request 3: hkdf error getting derived_key"; + handle->errmsg = "Setup request 3: hkdf error getting derived_key"; goto error; } // Append TLV-encoded public key to *data, which already has identifier and signature - append = tlv_new(); + append = pair_tlv_new(); append_len = REQUEST_BUFSIZE - data_len; - tlv_add_value(append, TLVType_PublicKey, sctx->public_key, sizeof(sctx->public_key)); - ret = tlv_format(append, data + data_len, &append_len); - tlv_free(append); + pair_tlv_add_value(append, TLVType_PublicKey, sctx->public_key, sizeof(sctx->public_key)); + ret = pair_tlv_format(append, data + data_len, &append_len); + pair_tlv_free(append); if (ret < 0) { - sctx->errmsg = "Setup request 3: error appending public key to TLV"; + handle->errmsg = "Setup request 3: error appending public key to TLV"; goto error; } data_len += append_len; @@ -1242,54 +1280,55 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) ret = encrypt_chacha(encrypted_data, data, data_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); if (ret < 0) { - sctx->errmsg = "Setup request 3: Could not encrypt"; + handle->errmsg = "Setup request 3: Could not encrypt"; goto error; } memcpy(encrypted_data + data_len, tag, sizeof(tag)); - tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG05].state, sizeof(pair_keys_map[PAIR_SETUP_MSG05].state)); - tlv_add_value(request, TLVType_EncryptedData, encrypted_data, encrypted_data_len); + pair_tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG05].state, sizeof(pair_keys_map[PAIR_SETUP_MSG05].state)); + pair_tlv_add_value(request, TLVType_EncryptedData, encrypted_data, encrypted_data_len); - data_len = REQUEST_BUFSIZE; // Re-using *data, so pass original length to tlv_format - ret = tlv_format(request, data, &data_len); + data_len = REQUEST_BUFSIZE; // Re-using *data, so pass original length to pair_tlv_format + ret = pair_tlv_format(request, data, &data_len); if (ret < 0) { - sctx->errmsg = "Setup request 3: error appending public key to TLV"; + handle->errmsg = "Setup request 3: error appending public key to TLV"; goto error; } *len = data_len; free(encrypted_data); - tlv_free(request); + pair_tlv_free(request); return data; error: free(encrypted_data); - tlv_free(request); + pair_tlv_free(request); free(data); return NULL; } static int -pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) { - tlv_values_t *response; - tlv_t *pk; - tlv_t *salt; + struct pair_client_setup_context *sctx = &handle->sctx.client; + pair_tlv_values_t *response; + pair_tlv_t *pk; + pair_tlv_t *salt; - response = response_process(data, data_len, &sctx->errmsg); + response = message_process(data, data_len, &handle->errmsg); if (!response) { return -1; } - pk = tlv_get_value(response, TLVType_PublicKey); - salt = tlv_get_value(response, TLVType_Salt); + pk = pair_tlv_get_value(response, TLVType_PublicKey); + salt = pair_tlv_get_value(response, TLVType_Salt); if (!pk || !salt) { - sctx->errmsg = "Setup response 1: Missing or invalid pk/salt"; + handle->errmsg = "Setup response 1: Missing or invalid pk/salt"; goto error; } @@ -1301,30 +1340,31 @@ pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_ sctx->salt = malloc(sctx->salt_len); memcpy(sctx->salt, salt->value, sctx->salt_len); - tlv_free(response); + pair_tlv_free(response); return 0; error: - tlv_free(response); + pair_tlv_free(response); return -1; } static int -pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) { - tlv_values_t *response; - tlv_t *proof; + struct pair_client_setup_context *sctx = &handle->sctx.client; + pair_tlv_values_t *response; + pair_tlv_t *proof; - response = response_process(data, data_len, &sctx->errmsg); + response = message_process(data, data_len, &handle->errmsg); if (!response) { return -1; } - proof = tlv_get_value(response, TLVType_Proof); + proof = pair_tlv_get_value(response, TLVType_Proof); if (!proof) { - sctx->errmsg = "Setup response 2: Missing proof"; + handle->errmsg = "Setup response 2: Missing proof"; goto error; } @@ -1336,27 +1376,28 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_ srp_user_verify_session(sctx->user, (const unsigned char *)sctx->M2); if (!srp_user_is_authenticated(sctx->user)) { - sctx->errmsg = "Setup response 2: Server authentication failed"; + handle->errmsg = "Setup response 2: Server authentication failed"; goto error; } - tlv_free(response); + pair_tlv_free(response); - if (sctx->type == &pair_homekit_transient) - sctx->setup_is_completed = 1; + if (handle->type == &pair_client_homekit_transient) + handle->setup_is_completed = 1; return 0; error: - tlv_free(response); + pair_tlv_free(response); return -1; } static int -pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) { - tlv_values_t *response; - tlv_t *encrypted_data; + struct pair_client_setup_context *sctx = &handle->sctx.client; + pair_tlv_values_t *response; + pair_tlv_t *encrypted_data; uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; uint8_t derived_key[32]; @@ -1366,37 +1407,37 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ int session_key_len; int ret; - response = response_process(data, data_len, &sctx->errmsg); + response = message_process(data, data_len, &handle->errmsg); if (!response) { return -1; } - encrypted_data = tlv_get_value(response, TLVType_EncryptedData); + encrypted_data = pair_tlv_get_value(response, TLVType_EncryptedData); if (!encrypted_data) { - sctx->errmsg = "Setup response 3: Missing encrypted_data"; + handle->errmsg = "Setup response 3: Missing encrypted_data"; goto error; } session_key = srp_user_get_session_key(sctx->user, &session_key_len); if (!session_key) { - sctx->errmsg = "Setup response 3: No valid session key"; + handle->errmsg = "Setup response 3: No valid session key"; goto error; } ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, 64, PAIR_SETUP_MSG06); // TODO is session_key_len always 64? if (ret < 0) { - sctx->errmsg = "Setup response 3: hkdf error getting derived_key"; + handle->errmsg = "Setup response 3: hkdf error getting derived_key"; goto error; } // encrypted_data->value consists of the encrypted payload + the auth tag if (encrypted_data->size < AUTHTAG_LENGTH) { - sctx->errmsg = "Setup response 3: Invalid encrypted data"; + handle->errmsg = "Setup response 3: Invalid encrypted data"; goto error; } @@ -1409,12 +1450,12 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ ret = decrypt_chacha(decrypted_data, encrypted_data->value, encrypted_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); if (ret < 0) { - sctx->errmsg = "Setup response 3: Decryption error"; + handle->errmsg = "Setup response 3: Decryption error"; goto error; } - tlv_free(response); - response = response_process(decrypted_data, encrypted_len, &sctx->errmsg); + pair_tlv_free(response); + response = message_process(decrypted_data, encrypted_len, &handle->errmsg); if (!response) { goto error; @@ -1423,36 +1464,37 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ // TODO check identifier and signature - we get an identifier (36), a public key (32) and a signature (64) free(decrypted_data); - tlv_free(response); + pair_tlv_free(response); - sctx->setup_is_completed = 1; + handle->setup_is_completed = 1; return 0; error: free(decrypted_data); - tlv_free(response); + pair_tlv_free(response); return -1; } static int -pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx) +pair_client_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *handle) { + struct pair_client_setup_context *sctx = &handle->sctx.client; const uint8_t *session_key; int session_key_len; - if (sctx->type == &pair_homekit_normal) + if (handle->type == &pair_client_homekit_normal) { // Last 32 bytes of private key should match public key, but check assumption if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0) { - sctx->errmsg = "Pair setup result: Unexpected keys, private key does not match public key"; + handle->errmsg = "Pair setup result: Unexpected keys, private key does not match public key"; return -1; } *key = sctx->private_key; *key_len = sizeof(sctx->private_key); return 0; } - if (sctx->type == &pair_homekit_transient) + if (handle->type == &pair_client_homekit_transient) { session_key = srp_user_get_session_key(sctx->user, &session_key_len); *key = session_key; @@ -1463,51 +1505,98 @@ pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_contex return -1; } -static uint8_t * -pair_verify_request1(size_t *len, struct pair_verify_context *vctx) +static int +pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, const char *device_id) { + struct pair_client_verify_context *vctx = &handle->vctx.client; + char hex[] = { 0, 0, 0 }; + size_t hexkey_len; + const char *ptr; + int i; + + if (sodium_init() == -1) + return -1; + + if (!hexkey) + return -1; + + hexkey_len = strlen(hexkey); + + if (hexkey_len != 2 * sizeof(vctx->client_private_key)) + return -1; + + if (device_id && strlen(device_id) != 16) + return -1; + + if (device_id) + memcpy(vctx->device_id, device_id, strlen(device_id)); + + ptr = hexkey; + for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2) + { + hex[0] = ptr[0]; + hex[1] = ptr[1]; + vctx->client_private_key[i] = strtol(hex, NULL, 16); + } + + ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key); + for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2) + { + hex[0] = ptr[0]; + hex[1] = ptr[1]; + vctx->client_public_key[i] = strtol(hex, NULL, 16); + } + + return 0; +} + +static uint8_t * +pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) +{ + struct pair_client_verify_context *vctx = &handle->vctx.client; const uint8_t basepoint[32] = {9}; - tlv_values_t *request; + pair_tlv_values_t *request; uint8_t *data; size_t data_len; int ret; data_len = REQUEST_BUFSIZE; data = malloc(data_len); - request = tlv_new(); + request = pair_tlv_new(); ret = crypto_scalarmult(vctx->client_eph_public_key, vctx->client_eph_private_key, basepoint); if (ret < 0) { - vctx->errmsg = "Verify request 1: Curve 25519 returned an error"; + handle->errmsg = "Verify request 1: Curve 25519 returned an error"; goto error; } - tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_VERIFY_MSG01].state, sizeof(pair_keys_map[PAIR_VERIFY_MSG01].state)); - tlv_add_value(request, TLVType_PublicKey, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key)); + pair_tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_VERIFY_MSG01].state, sizeof(pair_keys_map[PAIR_VERIFY_MSG01].state)); + pair_tlv_add_value(request, TLVType_PublicKey, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key)); - ret = tlv_format(request, data, &data_len); + ret = pair_tlv_format(request, data, &data_len); if (ret < 0) { - vctx->errmsg = "Verify request 1: tlv_format returned an error"; + handle->errmsg = "Verify request 1: pair_tlv_format returned an error"; goto error; } *len = data_len; - tlv_free(request); + pair_tlv_free(request); return data; error: - tlv_free(request); + pair_tlv_free(request); free(data); return NULL; } static uint8_t * -pair_verify_request2(size_t *len, struct pair_verify_context *vctx) +pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) { - tlv_values_t *request; + struct pair_client_verify_context *vctx = &handle->vctx.client; + pair_tlv_values_t *request; uint8_t *data; size_t data_len; uint8_t nonce[NONCE_LENGTH] = { 0 }; @@ -1519,20 +1608,20 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) data_len = REQUEST_BUFSIZE; data = malloc(data_len); - request = tlv_new(); + request = pair_tlv_new(); ret = create_and_sign_device_info(data, &data_len, vctx->device_id, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key), vctx->server_eph_public_key, sizeof(vctx->server_eph_public_key), vctx->client_private_key); if (ret < 0) { - vctx->errmsg = "Verify request 2: error creating signed device info"; + handle->errmsg = "Verify request 2: error creating signed device info"; goto error; } ret = hkdf_extract_expand(derived_key, sizeof(derived_key), vctx->shared_secret, sizeof(vctx->shared_secret), PAIR_VERIFY_MSG03); if (ret < 0) { - vctx->errmsg = "Verify request 2: hkdf error getting derived_key"; + handle->errmsg = "Verify request 2: hkdf error getting derived_key"; goto error; } @@ -1544,42 +1633,43 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) ret = encrypt_chacha(encrypted_data, data, data_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); if (ret < 0) { - vctx->errmsg = "Verify request 2: Could not encrypt"; + handle->errmsg = "Verify request 2: Could not encrypt"; goto error; } memcpy(encrypted_data + data_len, tag, sizeof(tag)); - tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_VERIFY_MSG03].state, sizeof(pair_keys_map[PAIR_VERIFY_MSG03].state)); - tlv_add_value(request, TLVType_EncryptedData, encrypted_data, encrypted_data_len); + pair_tlv_add_value(request, TLVType_State, &pair_keys_map[PAIR_VERIFY_MSG03].state, sizeof(pair_keys_map[PAIR_VERIFY_MSG03].state)); + pair_tlv_add_value(request, TLVType_EncryptedData, encrypted_data, encrypted_data_len); - data_len = REQUEST_BUFSIZE; // Re-using *data, so pass original length to tlv_format - ret = tlv_format(request, data, &data_len); + data_len = REQUEST_BUFSIZE; // Re-using *data, so pass original length to pair_tlv_format + ret = pair_tlv_format(request, data, &data_len); if (ret < 0) { - vctx->errmsg = "Verify request 2: tlv_format returned an error"; + handle->errmsg = "Verify request 2: pair_tlv_format returned an error"; goto error; } *len = data_len; free(encrypted_data); - tlv_free(request); + pair_tlv_free(request); return data; error: free(encrypted_data); - tlv_free(request); + pair_tlv_free(request); free(data); return NULL; } static int -pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) +pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) { - tlv_values_t *response; - tlv_t *encrypted_data; - tlv_t *public_key; + struct pair_client_verify_context *vctx = &handle->vctx.client; + pair_tlv_values_t *response; + pair_tlv_t *encrypted_data; + pair_tlv_t *public_key; uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; uint8_t derived_key[32]; @@ -1587,23 +1677,23 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz uint8_t *decrypted_data = NULL; int ret; - response = response_process(data, data_len, &vctx->errmsg); + response = message_process(data, data_len, &handle->errmsg); if (!response) { return -1; } - encrypted_data = tlv_get_value(response, TLVType_EncryptedData); + encrypted_data = pair_tlv_get_value(response, TLVType_EncryptedData); if (!encrypted_data) { - vctx->errmsg = "Verify response 1: Missing encrypted_data"; + handle->errmsg = "Verify response 1: Missing encrypted_data"; goto error; } - public_key = tlv_get_value(response, TLVType_PublicKey); + public_key = pair_tlv_get_value(response, TLVType_PublicKey); if (!public_key || public_key->size != sizeof(vctx->server_eph_public_key)) { - vctx->errmsg = "Verify response 1: Missing or invalid public_key"; + handle->errmsg = "Verify response 1: Missing or invalid public_key"; goto error; } @@ -1611,21 +1701,21 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz ret = crypto_scalarmult(vctx->shared_secret, vctx->client_eph_private_key, vctx->server_eph_public_key); if (ret < 0) { - vctx->errmsg = "Verify response 1: Curve 25519 returned an error"; + handle->errmsg = "Verify response 1: Curve 25519 returned an error"; goto error; } ret = hkdf_extract_expand(derived_key, sizeof(derived_key), vctx->shared_secret, sizeof(vctx->shared_secret), PAIR_VERIFY_MSG02); if (ret < 0) { - vctx->errmsg = "Verify response 1: hkdf error getting derived_key"; + handle->errmsg = "Verify response 1: hkdf error getting derived_key"; goto error; } // encrypted_data->value consists of the encrypted payload + the auth tag if (encrypted_data->size < AUTHTAG_LENGTH) { - vctx->errmsg = "Verify response 1: Invalid encrypted data"; + handle->errmsg = "Verify response 1: Invalid encrypted data"; goto error; } @@ -1638,12 +1728,12 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz ret = decrypt_chacha(decrypted_data, encrypted_data->value, encrypted_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); if (ret < 0) { - vctx->errmsg = "Verify response 1: Decryption error"; + handle->errmsg = "Verify response 1: Decryption error"; goto error; } - tlv_free(response); - response = response_process(decrypted_data, encrypted_len, &vctx->errmsg); + pair_tlv_free(response); + response = message_process(decrypted_data, encrypted_len, &handle->errmsg); if (!response) { goto error; @@ -1652,22 +1742,277 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz // TODO check identifier and signature free(decrypted_data); - tlv_free(response); + pair_tlv_free(response); return 0; error: free(decrypted_data); - tlv_free(response); + pair_tlv_free(response); return -1; } static int -pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) +pair_client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) { // TODO actually check response return 0; } +static int +pair_client_verify_result(const uint8_t **key, size_t *key_len, struct pair_verify_context *handle) +{ + struct pair_client_verify_context *vctx = &handle->vctx.client; + + *key = vctx->shared_secret; + *key_len = sizeof(vctx->shared_secret); + + return 0; +} + + +/* ------------------------- SERVER IMPLEMENTATION -------------------------- */ + +static int +pair_server_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + + if (sodium_init() == -1) + return -1; + + // Only transient pairing currently implemented + if (handle->type != &pair_server_homekit_transient) + return -1; + + if (!pin) + pin = "3939"; + + if (device_id && strlen(device_id) != 16) + return -1; + + memcpy(sctx->pin, pin, sizeof(sctx->pin)); + + if (device_id) + memcpy(sctx->device_id, device_id, strlen(device_id)); + + return 0; +} + +static void +pair_server_setup_free(struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + + srp_verifier_free(sctx->verifier); + + free(sctx->pkA); + free(sctx->pkB); + free(sctx->b); + free(sctx->M1); + free(sctx->v); + free(sctx->salt); +} + +static int +pair_server_setup_request1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + pair_tlv_values_t *request; + pair_tlv_t *method; + pair_tlv_t *type; + int ret; + + request = message_process(data, data_len, &handle->errmsg); + if (!request) + { + goto error; + } + + method = pair_tlv_get_value(request, TLVType_Method); + if (!method || method->size != 1 || method->value[0] != 0) + { + handle->errmsg = "Setup request 1: Missing or unexpected pairing method in TLV"; + goto error; + } + + type = pair_tlv_get_value(request, TLVType_Flags); + if (!type || type->size != 1 || type->value[0] != PairingFlagsTransient) + { + handle->errmsg = "Setup request 1: No support for the non-transient pairing requested by client"; + goto error; + } + + // Note this is modified to return a 16 byte salt + ret = srp_create_salted_verification_key(HASH_SHA512, SRP_NG_3072, USERNAME, (unsigned char *)sctx->pin, sizeof(sctx->pin), + &sctx->salt, &sctx->salt_len, &sctx->v, &sctx->v_len, NULL, NULL); + if (ret < 0) + { + handle->errmsg = "Setup request 1: Could not create verification key"; + goto error; + } + + ret = srp_verifier_start_authentication(HASH_SHA512, SRP_NG_3072, sctx->v, sctx->v_len, + &sctx->b, &sctx->b_len, &sctx->pkB, &sctx->pkB_len, NULL, NULL); + if (ret < 0) + { + handle->errmsg = "Setup request 1: Could not compute B"; + goto error; + } + + pair_tlv_free(request); + return 0; + + error: + pair_tlv_free(request); + return -1; +} + +static int +pair_server_setup_request2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + pair_tlv_values_t *request; + pair_tlv_t *pk; + pair_tlv_t *proof; + + request = message_process(data, data_len, &handle->errmsg); + if (!request) + { + goto error; + } + + pk = pair_tlv_get_value(request, TLVType_PublicKey); + proof = pair_tlv_get_value(request, TLVType_Proof); + if (!pk || !proof) + { + handle->errmsg = "Setup request 2: Missing pkA or proof"; + goto error; + } + + sctx->pkA_len = pk->size; // 384 + sctx->pkA = malloc(sctx->pkA_len); + memcpy(sctx->pkA, pk->value, sctx->pkA_len); + + sctx->M1_len = proof->size; // 64 + sctx->M1 = malloc(sctx->M1_len); + memcpy(sctx->M1, proof->value, sctx->M1_len); + + sctx->verifier = srp_verifier_new(HASH_SHA512, SRP_NG_3072, USERNAME, sctx->salt, sctx->salt_len, sctx->v, sctx->v_len, + sctx->pkA, sctx->pkA_len, sctx->b, sctx->b_len, sctx->pkB, sctx->pkB_len, NULL, NULL); + if (!sctx->verifier) + { + handle->errmsg = "Setup request 2: Incorrect verifier"; + goto error; + } + + sctx->M2_len = 64; // 512 bit hash + srp_verifier_verify_session(sctx->verifier, sctx->M1, &sctx->M2); + if (!sctx->M2) + { + handle->errmsg = "Setup request 2: Incorrect M2"; + goto error; + } + + handle->setup_is_completed = 1; + pair_tlv_free(request); + return 0; + + error: + pair_tlv_free(request); + return -1; +} + +static uint8_t * +pair_server_setup_response1(size_t *len, struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + pair_tlv_values_t *response; + uint8_t *data; + size_t data_len; + int ret; + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG02].state, sizeof(pair_keys_map[PAIR_SETUP_MSG02].state)); + pair_tlv_add_value(response, TLVType_Salt, sctx->salt, sctx->salt_len); // 16 + pair_tlv_add_value(response, TLVType_PublicKey, sctx->pkB, sctx->pkB_len); // 384 + + ret = pair_tlv_format(response, data, &data_len); + if (ret < 0) + { + handle->errmsg = "Setup response 1: pair_tlv_format returned an error"; + goto error; + } + + *len = data_len; + + pair_tlv_free(response); + return data; + + error: + pair_tlv_free(response); + free(data); + return NULL; +} + +static uint8_t * +pair_server_setup_response2(size_t *len, struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + pair_tlv_values_t *response; + uint8_t *data; + size_t data_len; + int ret; + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG04].state, sizeof(pair_keys_map[PAIR_SETUP_MSG04].state)); + pair_tlv_add_value(response, TLVType_Proof, sctx->M2, sctx->M2_len); // 384 + + ret = pair_tlv_format(response, data, &data_len); + if (ret < 0) + { + handle->errmsg = "Setup response 2: pair_tlv_format returned an error"; + goto error; + } + + *len = data_len; + + pair_tlv_free(response); + return data; + + error: + pair_tlv_free(response); + free(data); + return NULL; +} + +static int +pair_server_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + const uint8_t *session_key; + int session_key_len; + + session_key = srp_verifier_get_session_key(sctx->verifier, &session_key_len); + if (!session_key) + { + handle->errmsg = "Pair setup result: Could not compute session key"; + return -1; + } + + *key = session_key; + *key_len = session_key_len; + return 0; +} + + +/* ----------------------- CIPHERING IMPLEMENTATION ------------------------- */ + static void pair_cipher_free(struct pair_cipher_context *cctx) { @@ -1839,56 +2184,117 @@ pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, si return cipher_block - ciphertext; } -const struct pair_definition pair_homekit_normal = +static int +pair_state_get(const char **errmsg, const uint8_t *data, size_t data_len) { - .pair_setup_new = pair_setup_new, - .pair_setup_free = pair_setup_free, - .pair_setup_result = pair_setup_result, + pair_tlv_values_t *message; + pair_tlv_t *state; + int ret; - .pair_setup_request1 = pair_setup_request1, - .pair_setup_request2 = pair_setup_request2, - .pair_setup_request3 = pair_setup_request3, + message = message_process(data, data_len, errmsg); + if (!message) + { + goto error; + } - .pair_setup_response1 = pair_setup_response1, - .pair_setup_response2 = pair_setup_response2, - .pair_setup_response3 = pair_setup_response3, + state = pair_tlv_get_value(message, TLVType_State); + if (!state || state->size != 1) + { + *errmsg = "Could not get message state"; + goto error; + } - .pair_verify_request1 = pair_verify_request1, - .pair_verify_request2 = pair_verify_request2, + ret = state->value[0]; - .pair_verify_response1 = pair_verify_response1, - .pair_verify_response2 = pair_verify_response2, + pair_tlv_free(message); + return ret; + + error: + pair_tlv_free(message); + return -1; +} + +const struct pair_definition pair_client_homekit_normal = +{ + .pair_setup_new = pair_client_setup_new, + .pair_setup_free = pair_client_setup_free, + .pair_setup_result = pair_client_setup_result, + + .pair_setup_request1 = pair_client_setup_request1, + .pair_setup_request2 = pair_client_setup_request2, + .pair_setup_request3 = pair_client_setup_request3, + + .pair_setup_response1 = pair_client_setup_response1, + .pair_setup_response2 = pair_client_setup_response2, + .pair_setup_response3 = pair_client_setup_response3, + + .pair_verify_new = pair_client_verify_new, + .pair_verify_result = pair_client_verify_result, + + .pair_verify_request1 = pair_client_verify_request1, + .pair_verify_request2 = pair_client_verify_request2, + + .pair_verify_response1 = pair_client_verify_response1, + .pair_verify_response2 = pair_client_verify_response2, .pair_cipher_new = pair_cipher_new, .pair_cipher_free = pair_cipher_free, .pair_encrypt = pair_encrypt, .pair_decrypt = pair_decrypt, + + .pair_state_get = pair_state_get, }; -const struct pair_definition pair_homekit_transient = +const struct pair_definition pair_client_homekit_transient = { - .pair_setup_new = pair_setup_new, - .pair_setup_free = pair_setup_free, - .pair_setup_result = pair_setup_result, + .pair_setup_new = pair_client_setup_new, + .pair_setup_free = pair_client_setup_free, + .pair_setup_result = pair_client_setup_result, - .pair_setup_request1 = pair_setup_request1, - .pair_setup_request2 = pair_setup_request2, - .pair_setup_request3 = pair_setup_request3, + .pair_setup_request1 = pair_client_setup_request1, + .pair_setup_request2 = pair_client_setup_request2, + .pair_setup_request3 = pair_client_setup_request3, - .pair_setup_response1 = pair_setup_response1, - .pair_setup_response2 = pair_setup_response2, - .pair_setup_response3 = pair_setup_response3, + .pair_setup_response1 = pair_client_setup_response1, + .pair_setup_response2 = pair_client_setup_response2, + .pair_setup_response3 = pair_client_setup_response3, - .pair_verify_request1 = pair_verify_request1, - .pair_verify_request2 = pair_verify_request2, + .pair_verify_new = pair_client_verify_new, + .pair_verify_result = pair_client_verify_result, - .pair_verify_response1 = pair_verify_response1, - .pair_verify_response2 = pair_verify_response2, + .pair_verify_request1 = pair_client_verify_request1, + .pair_verify_request2 = pair_client_verify_request2, + + .pair_verify_response1 = pair_client_verify_response1, + .pair_verify_response2 = pair_client_verify_response2, .pair_cipher_new = pair_cipher_new, .pair_cipher_free = pair_cipher_free, .pair_encrypt = pair_encrypt, .pair_decrypt = pair_decrypt, + + .pair_state_get = pair_state_get, +}; + +const struct pair_definition pair_server_homekit_transient = +{ + .pair_setup_new = pair_server_setup_new, + .pair_setup_free = pair_server_setup_free, + .pair_setup_result = pair_server_setup_result, + + .pair_setup_request1 = pair_server_setup_response1, + .pair_setup_request2 = pair_server_setup_response2, + + .pair_setup_response1 = pair_server_setup_request1, + .pair_setup_response2 = pair_server_setup_request2, + + .pair_cipher_new = pair_cipher_new, + .pair_cipher_free = pair_cipher_free, + + .pair_encrypt = pair_encrypt, + .pair_decrypt = pair_decrypt, + + .pair_state_get = pair_state_get, };