From 088e26c1f07a467b27f2891abf98fdcd395d4e47 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sat, 4 Dec 2021 23:05:33 +0100 Subject: [PATCH] [airplay] Fix rare pairing bug due to incorrect SRP padding (ref #1280) Update pair_ap to version 0.11, which it fixes an issue where some randomized values of SRP "a" are short enough that they require padding. --- src/outputs/airplay.c | 20 +- src/outputs/raop.c | 6 +- src/pair_ap/pair-internal.h | 131 +++- src/pair_ap/pair.c | 329 +++++++-- src/pair_ap/pair.h | 201 +++++- src/pair_ap/pair_fruit.c | 132 ++-- src/pair_ap/pair_homekit.c | 1364 ++++++++++++++++++++++++++++------- 7 files changed, 1707 insertions(+), 476 deletions(-) diff --git a/src/outputs/airplay.c b/src/outputs/airplay.c index bec0a981..2ebeed57 100644 --- a/src/outputs/airplay.c +++ b/src/outputs/airplay.c @@ -2721,7 +2721,7 @@ payload_make_pair_setup1(struct evrtsp_request *req, struct airplay_session *rs, snprintf(device_id_hex, sizeof(device_id_hex), "%016" PRIX64, airplay_device_id); - rs->pair_setup_ctx = pair_setup_new(rs->pair_type, pin, device_id_hex); + rs->pair_setup_ctx = pair_setup_new(rs->pair_type, pin, NULL, NULL, device_id_hex); if (!rs->pair_setup_ctx) { DPRINTF(E_LOG, L_AIRPLAY, "Out of memory for verification setup context\n"); @@ -2757,7 +2757,7 @@ payload_make_pair_verify1(struct evrtsp_request *req, struct airplay_session *rs snprintf(device_id_hex, sizeof(device_id_hex), "%016" PRIX64, airplay_device_id); - rs->pair_verify_ctx = pair_verify_new(rs->pair_type, device->auth_key, device_id_hex); + rs->pair_verify_ctx = pair_verify_new(rs->pair_type, device->auth_key, NULL, NULL, device_id_hex); if (!rs->pair_verify_ctx) { DPRINTF(E_LOG, L_AIRPLAY, "Out of memory for verification verify context\n"); @@ -3179,8 +3179,7 @@ static enum airplay_seq_type response_handler_pair_setup2(struct evrtsp_request *req, struct airplay_session *rs) { enum airplay_seq_type seq_type; - const uint8_t *shared_secret; - size_t shared_secret_len; + struct pair_result *result; int ret; seq_type = response_handler_pair_generic(2, req, rs); @@ -3190,14 +3189,14 @@ response_handler_pair_setup2(struct evrtsp_request *req, struct airplay_session 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); + ret = pair_setup_result(NULL, &result, rs->pair_setup_ctx); if (ret < 0) { DPRINTF(E_LOG, L_AIRPLAY, "Transient setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx)); goto error; } - ret = session_cipher_setup(rs, shared_secret, shared_secret_len); + ret = session_cipher_setup(rs, result->shared_secret, result->shared_secret_len); if (ret < 0) { DPRINTF(E_LOG, L_AIRPLAY, "Pair transient error setting up encryption for '%s'\n", rs->devname); @@ -3223,7 +3222,7 @@ response_handler_pair_setup3(struct evrtsp_request *req, struct airplay_session if (seq_type != AIRPLAY_SEQ_CONTINUE) return seq_type; - ret = pair_setup_result(&authorization_key, NULL, NULL, rs->pair_setup_ctx); + ret = pair_setup_result(&authorization_key, NULL, rs->pair_setup_ctx); if (ret < 0) { DPRINTF(E_LOG, L_AIRPLAY, "Pair setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx)); @@ -3278,22 +3277,21 @@ response_handler_pair_verify2(struct evrtsp_request *req, struct airplay_session { struct output_device *device; enum airplay_seq_type seq_type; - const uint8_t *shared_secret; - size_t shared_secret_len; + struct pair_result *result; int ret; seq_type = response_handler_pair_generic(5, req, rs); if (seq_type != AIRPLAY_SEQ_CONTINUE) goto error; - ret = pair_verify_result(&shared_secret, &shared_secret_len, rs->pair_verify_ctx); + ret = pair_verify_result(&result, rs->pair_verify_ctx); if (ret < 0) { DPRINTF(E_LOG, L_AIRPLAY, "Pair verify result error: %s\n", pair_verify_errmsg(rs->pair_verify_ctx)); goto error; } - ret = session_cipher_setup(rs, shared_secret, shared_secret_len); + ret = session_cipher_setup(rs, result->shared_secret, result->shared_secret_len); if (ret < 0) { DPRINTF(E_LOG, L_AIRPLAY, "Pair verify error setting up encryption for '%s'\n", rs->devname); diff --git a/src/outputs/raop.c b/src/outputs/raop.c index abd366c2..4f0968c3 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -3846,7 +3846,7 @@ raop_pair_verify(struct raop_session *rs) if (!device) goto error; - CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_CLIENT_FRUIT, device->auth_key, NULL)); + CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_CLIENT_FRUIT, device->auth_key, NULL, NULL, NULL)); ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1); if (ret < 0) @@ -3872,7 +3872,7 @@ raop_cb_pair_setup_step3(struct evrtsp_request *req, void *arg) if (ret < 0) goto out; - ret = pair_setup_result(&authorization_key, NULL, NULL, rs->pair_setup_ctx); + ret = pair_setup_result(&authorization_key, NULL, rs->pair_setup_ctx); if (ret < 0) { DPRINTF(E_LOG, L_RAOP, "Verification setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx)); @@ -3955,7 +3955,7 @@ raop_pair_setup(struct raop_session *rs, const char *pin) { int ret; - rs->pair_setup_ctx = pair_setup_new(PAIR_CLIENT_FRUIT, pin, NULL); + rs->pair_setup_ctx = pair_setup_new(PAIR_CLIENT_FRUIT, pin, NULL, NULL, NULL); if (!rs->pair_setup_ctx) { DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n"); diff --git a/src/pair_ap/pair-internal.h b/src/pair_ap/pair-internal.h index 1d510426..3de74daa 100644 --- a/src/pair_ap/pair-internal.h +++ b/src/pair_ap/pair-internal.h @@ -1,6 +1,13 @@ #include +#include #include +#include "pair.h" + +#define RETURN_ERROR(s, m) \ + do { handle->status = (s); handle->errmsg = (m); goto error; } while(0) + + struct SRPUser; struct SRPVerifier; @@ -8,8 +15,14 @@ struct pair_client_setup_context { struct SRPUser *user; - char pin[4]; - char device_id[17]; // Incl. zero term + uint8_t pin[4]; + char device_id[PAIR_AP_DEVICE_ID_LEN_MAX]; + + pair_cb add_cb; + void *add_cb_arg; + + uint8_t public_key[crypto_sign_PUBLICKEYBYTES]; + uint8_t private_key[crypto_sign_SECRETKEYBYTES]; const uint8_t *pkA; int pkA_len; @@ -25,8 +38,6 @@ struct pair_client_setup_context uint8_t *salt; uint64_t salt_len; - uint8_t public_key[crypto_sign_PUBLICKEYBYTES]; - uint8_t private_key[crypto_sign_SECRETKEYBYTES]; // We don't actually use the server's epk and authtag for anything uint8_t *epk; @@ -39,8 +50,16 @@ struct pair_server_setup_context { struct SRPVerifier *verifier; - char pin[4]; - char device_id[17]; // Incl. zero term + uint8_t pin[4]; + char device_id[PAIR_AP_DEVICE_ID_LEN_MAX]; + + pair_cb add_cb; + void *add_cb_arg; + + uint8_t public_key[crypto_sign_PUBLICKEYBYTES]; + uint8_t private_key[crypto_sign_SECRETKEYBYTES]; + + bool is_transient; uint8_t *pkA; uint64_t pkA_len; @@ -64,13 +83,24 @@ struct pair_server_setup_context int salt_len; }; +enum pair_status +{ + PAIR_STATUS_IN_PROGRESS, + PAIR_STATUS_COMPLETED, + PAIR_STATUS_AUTH_FAILED, + PAIR_STATUS_INVALID, +}; + struct pair_setup_context { struct pair_definition *type; - int setup_is_completed; + enum pair_status status; const char *errmsg; + struct pair_result result; + char result_str[256]; // Holds the hex string version of the keys that pair_verify_new() needs + // Hex-formatet concatenation of public + private, 0-terminated char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1]; @@ -83,30 +113,59 @@ struct pair_setup_context struct pair_client_verify_context { - char device_id[17]; // Incl. zero term + char device_id[PAIR_AP_DEVICE_ID_LEN_MAX]; - uint8_t server_eph_public_key[32]; - uint8_t server_public_key[64]; + // These are the keys that were registered with the server in pair-setup + uint8_t client_public_key[crypto_sign_PUBLICKEYBYTES]; // 32 + uint8_t client_private_key[crypto_sign_SECRETKEYBYTES]; // 64 - uint8_t client_public_key[crypto_sign_PUBLICKEYBYTES]; - uint8_t client_private_key[crypto_sign_SECRETKEYBYTES]; + bool verify_server_signature; + uint8_t server_fruit_public_key[64]; // Not sure why it has this length in fruit mode + uint8_t server_public_key[crypto_sign_PUBLICKEYBYTES]; // 32 - uint8_t client_eph_public_key[32]; - uint8_t client_eph_private_key[32]; + // For establishing the shared secret for encrypted communication + uint8_t client_eph_public_key[crypto_box_PUBLICKEYBYTES]; // 32 + uint8_t client_eph_private_key[crypto_box_SECRETKEYBYTES]; // 32 - uint8_t shared_secret[32]; + uint8_t server_eph_public_key[crypto_box_PUBLICKEYBYTES]; // 32 + + uint8_t shared_secret[crypto_scalarmult_BYTES]; // 32 +}; + +struct pair_server_verify_context +{ + char device_id[PAIR_AP_DEVICE_ID_LEN_MAX]; + + // Same keys as used for pair-setup, derived from device_id + uint8_t server_public_key[crypto_sign_PUBLICKEYBYTES]; // 32 + uint8_t server_private_key[crypto_sign_SECRETKEYBYTES]; // 64 + + bool verify_client_signature; + pair_cb get_cb; + void *get_cb_arg; + + // For establishing the shared secret for encrypted communication + uint8_t server_eph_public_key[crypto_box_PUBLICKEYBYTES]; // 32 + uint8_t server_eph_private_key[crypto_box_SECRETKEYBYTES]; // 32 + + uint8_t client_eph_public_key[crypto_box_PUBLICKEYBYTES]; // 32 + + uint8_t shared_secret[crypto_scalarmult_BYTES]; // 32 }; struct pair_verify_context { struct pair_definition *type; - int verify_is_completed; + enum pair_status status; const char *errmsg; + struct pair_result result; + union pair_verify_union { struct pair_client_verify_context client; + struct pair_server_verify_context server; } vctx; }; @@ -129,38 +188,49 @@ struct pair_cipher_context struct pair_definition { - int (*pair_setup_new)(struct pair_setup_context *sctx, const char *pin, const char *device_id); + int (*pair_setup_new)(struct pair_setup_context *sctx, const char *pin, pair_cb add_cb, void *cb_arg, 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); + int (*pair_setup_result)(struct pair_setup_context *sctx); uint8_t *(*pair_setup_request1)(size_t *len, struct pair_setup_context *sctx); uint8_t *(*pair_setup_request2)(size_t *len, struct pair_setup_context *sctx); uint8_t *(*pair_setup_request3)(size_t *len, struct pair_setup_context *sctx); - int (*pair_setup_response1)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len); - 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_setup_response1)(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); + int (*pair_setup_response2)(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); + int (*pair_setup_response3)(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); - int (*pair_verify_new)(struct pair_verify_context *vctx, const char *hexkey, const char *device_id); + int (*pair_verify_new)(struct pair_verify_context *vctx, const char *client_setup_keys, pair_cb cb, void *cb_arg, 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); + int (*pair_verify_result)(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); - int (*pair_verify_response1)(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len); - int (*pair_verify_response2)(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len); + int (*pair_verify_response1)(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len); + int (*pair_verify_response2)(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len); + + int (*pair_add)(uint8_t **out, size_t *out_len, pair_cb cb, void *cb_arg, const uint8_t *in, size_t in_len); + int (*pair_remove)(uint8_t **out, size_t *out_len, pair_cb cb, void *cb_arg, const uint8_t *in, size_t in_len); + int (*pair_list)(uint8_t **out, size_t *out_len, pair_list_cb cb, void *cb_arg, const uint8_t *in, size_t in_len); struct pair_cipher_context *(*pair_cipher_new)(struct pair_definition *type, int channel, const uint8_t *shared_secret, size_t shared_secret_len); void (*pair_cipher_free)(struct pair_cipher_context *cctx); - 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); + ssize_t (*pair_encrypt)(uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx); + ssize_t (*pair_decrypt)(uint8_t **plaintext, size_t *plaintext_len, const 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); + int (*pair_state_get)(const char **errmsg, const uint8_t *in, size_t in_len); + void (*pair_public_key_get)(uint8_t server_public_key[32], const char *device_id); }; +/* ----------------------------- INITIALIZATION ---------------------------- */ + +bool +is_initialized(void); + + /* -------------------- GCRYPT AND OPENSSL COMPABILITY --------------------- */ /* partly borrowed from ffmpeg (rtmpdh.c) */ @@ -297,7 +367,7 @@ int hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_len, const unsigned char *m2, int m2_len); bnum -H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2); +H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2, int padded_len); bnum H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes); @@ -314,4 +384,7 @@ hash_num(enum hash_alg alg, const bnum n, unsigned char *dest); #ifdef DEBUG_PAIR void hexdump(const char *msg, uint8_t *mem, size_t len); + +void +bnum_dump(const char *msg, bnum n); #endif diff --git a/src/pair_ap/pair.c b/src/pair_ap/pair.c index 4ebbd409..e444ab5b 100644 --- a/src/pair_ap/pair.c +++ b/src/pair_ap/pair.c @@ -25,6 +25,7 @@ #include #include #include // for isprint() +#include #include @@ -34,16 +35,37 @@ 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; +extern struct pair_definition pair_server_homekit; // Must be in sync with enum pair_type static struct pair_definition *pair[] = { &pair_client_fruit, &pair_client_homekit_normal, &pair_client_homekit_transient, - &pair_server_homekit_transient, + &pair_server_homekit, }; +/* ------------------------------ INITIALIZATION ---------------------------- */ + +bool +is_initialized(void) +{ + if (sodium_init() == -1) + return false; + +#if CONFIG_GCRYPT + // According to libgcrypt documentation: "It is important that these + // initialization steps are not done by a library but by the actual + // application. A library using Libgcrypt might want to check for finished + // initialization using:" + if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) + return false; +#endif + + return true; +} + + /* -------------------------- SHARED HASHING HELPERS ------------------------ */ int @@ -167,24 +189,27 @@ hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_le return hash_final(alg, &ctx, md); } +// See rfc5054 PAD() bnum -H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2) +H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2, int padded_len) { bnum bn; unsigned char *bin; unsigned char buff[SHA512_DIGEST_LENGTH]; int len_n1 = bnum_num_bytes(n1); int len_n2 = bnum_num_bytes(n2); - int nbytes = 2 * len_n1; + int nbytes = 2 * padded_len; + int offset_n1 = padded_len - len_n1; + int offset_n2 = nbytes - len_n2; - if ((len_n2 < 1) || (len_n2 > len_n1)) - return 0; + assert(len_n1 <= padded_len); + assert(len_n2 <= padded_len); - bin = calloc( 1, nbytes ); + bin = calloc(1, nbytes); - bnum_bn2bin(n1, bin, len_n1); - bnum_bn2bin(n2, bin + nbytes - len_n2, len_n2); - hash( alg, bin, nbytes, buff ); + bnum_bn2bin(n1, bin + offset_n1, len_n1); + bnum_bn2bin(n2, bin + offset_n2, len_n2); + hash(alg, bin, nbytes, buff); free(bin); bnum_bin2bn(bn, buff, hash_length(alg)); return bn; @@ -200,8 +225,8 @@ H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes) unsigned char *bin = malloc(nbytes); bnum_bn2bin(n, bin, len_n); - memcpy( bin + len_n, bytes, len_bytes ); - hash( alg, bin, nbytes, buff ); + memcpy(bin + len_n, bytes, len_bytes); + hash(alg, bin, nbytes, buff); free(bin); bnum_bin2bn(bn, buff, hash_length(alg)); return bn; @@ -268,13 +293,23 @@ hexdump(const char *msg, uint8_t *mem, size_t len) } } } + +void +bnum_dump(const char *msg, bnum n) +{ + int len_n = bnum_num_bytes(n); + uint8_t *bin = calloc(1, len_n); + bnum_bn2bin(n, bin, len_n); + hexdump(msg, bin, len_n); + free(bin); +} #endif /* ----------------------------------- API -----------------------------------*/ struct pair_setup_context * -pair_setup_new(enum pair_type type, const char *pin, const char *device_id) +pair_setup_new(enum pair_type type, const char *pin, pair_cb add_cb, void *cb_arg, const char *device_id) { struct pair_setup_context *sctx; @@ -287,7 +322,7 @@ pair_setup_new(enum pair_type type, const char *pin, const char *device_id) sctx->type = pair[type]; - if (pair[type]->pair_setup_new(sctx, pin, device_id) < 0) + if (pair[type]->pair_setup_new(sctx, pin, add_cb, cb_arg, device_id) < 0) { free(sctx); return NULL; @@ -314,6 +349,76 @@ pair_setup_errmsg(struct pair_setup_context *sctx) return sctx->errmsg; } +int +pair_setup(uint8_t **out, size_t *out_len, struct pair_setup_context *sctx, const uint8_t *in, size_t in_len) +{ + int state; + int ret = -1; + + if (!sctx->type->pair_state_get) + { + sctx->errmsg = "Getting pair state unsupported"; + return -1; + } + + *out = NULL; + *out_len = 0; + + state = sctx->type->pair_state_get(&sctx->errmsg, in, in_len); + if (state < 0) + return -1; + + switch (state) + { + case 0: + *out = pair_setup_request1(out_len, sctx); + break; + case 1: + ret = pair_setup_response1(sctx, in, in_len); + if (ret < 0) + break; + *out = pair_setup_request1(out_len, sctx); + break; + case 2: + ret = pair_setup_response1(sctx, in, in_len); + if (ret < 0) + break; + *out = pair_setup_request2(out_len, sctx); + break; + case 3: + ret = pair_setup_response2(sctx, in, in_len); + if (ret < 0) + break; + *out = pair_setup_request2(out_len, sctx); + break; + case 4: + ret = pair_setup_response2(sctx, in, in_len); + if (ret < 0) + break; + *out = pair_setup_request3(out_len, sctx); + break; + case 5: + ret = pair_setup_response3(sctx, in, in_len); + if (ret < 0) + break; + *out = pair_setup_request3(out_len, sctx); + break; + case 6: + ret = pair_setup_response3(sctx, in, in_len); + if (ret < 0) + break; + break; + default: + sctx->errmsg = "Setup: Unsupported state"; + ret = -1; + } + + if (ret < 0 || !(*out)) + return -1; + + return 0; +} + uint8_t * pair_setup_request1(size_t *len, struct pair_setup_context *sctx) { @@ -351,7 +456,7 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx) } int -pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len) { if (!sctx->type->pair_setup_response1) { @@ -359,11 +464,11 @@ pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_ return -1; } - return sctx->type->pair_setup_response1(sctx, data, data_len); + return sctx->type->pair_setup_response1(sctx, in, in_len); } int -pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len) { if (!sctx->type->pair_setup_response2) { @@ -371,11 +476,11 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_ return -1; } - return sctx->type->pair_setup_response2(sctx, data, data_len); + return sctx->type->pair_setup_response2(sctx, in, in_len); } int -pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len) +pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len) { if (!sctx->type->pair_setup_response3) { @@ -383,61 +488,36 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_ return -1; } - if (sctx->type->pair_setup_response3(sctx, data, data_len) != 0) + if (sctx->type->pair_setup_response3(sctx, in, in_len) != 0) return -1; return 0; } int -pair_setup_result(const char **hexkey, const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx) +pair_setup_result(const char **client_setup_keys, struct pair_result **result, struct pair_setup_context *sctx) { - const uint8_t *out_key; - size_t out_len; - char *ptr; - int i; - - if (!sctx->setup_is_completed) + if (sctx->status != PAIR_STATUS_COMPLETED) { - sctx->errmsg = "Setup result: The pair setup has not been completed"; + sctx->errmsg = "Setup result: Pair setup has not been completed"; return -1; } - if (!sctx->type->pair_setup_result) + if (sctx->type->pair_setup_result) { - sctx->errmsg = "Setup result: Unsupported"; - return -1; + if (sctx->type->pair_setup_result(sctx) != 0) + return -1; } - if (sctx->type->pair_setup_result(&out_key, &out_len, sctx) != 0) - { - return -1; - } - - if (2 * out_len + 1 > sizeof(sctx->auth_key)) - { - sctx->errmsg = "Setup result: Invalid key length"; - return -1; - } - - ptr = sctx->auth_key; - for (i = 0; i < out_len; i++) - ptr += sprintf(ptr, "%02x", out_key[i]); - *ptr = '\0'; - - if (key) - *key = out_key; - if (key_len) - *key_len = out_len; - if (hexkey) - *hexkey = sctx->auth_key; - + if (client_setup_keys) + *client_setup_keys = sctx->result_str; + if (result) + *result = &sctx->result; return 0; } - struct pair_verify_context * -pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id) +pair_verify_new(enum pair_type type, const char *client_setup_keys, pair_cb get_cb, void *cb_arg, const char *device_id) { struct pair_verify_context *vctx; @@ -450,7 +530,7 @@ pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id) vctx->type = pair[type]; - if (pair[type]->pair_verify_new(vctx, hexkey, device_id) < 0) + if (pair[type]->pair_verify_new(vctx, client_setup_keys, get_cb, cb_arg, device_id) < 0) { free(vctx); return NULL; @@ -477,6 +557,64 @@ pair_verify_errmsg(struct pair_verify_context *vctx) return vctx->errmsg; } +int +pair_verify(uint8_t **out, size_t *out_len, struct pair_verify_context *vctx, const uint8_t *in, size_t in_len) +{ + int state; + int ret = -1; + + if (!vctx->type->pair_state_get) + { + vctx->errmsg = "Getting pair state unsupported"; + return -1; + } + + *out = NULL; + *out_len = 0; + + state = vctx->type->pair_state_get(&vctx->errmsg, in, in_len); + if (state < 0) + return -1; + + switch (state) + { + case 0: + *out = pair_verify_request1(out_len, vctx); + break; + case 1: + ret = pair_verify_response1(vctx, in, in_len); + if (ret < 0) + break; + *out = pair_verify_request1(out_len, vctx); + break; + case 2: + ret = pair_verify_response1(vctx, in, in_len); + if (ret < 0) + break; + *out = pair_verify_request2(out_len, vctx); + break; + case 3: + ret = pair_verify_response2(vctx, in, in_len); + if (ret < 0) + break; + *out = pair_verify_request2(out_len, vctx); + break; + case 4: + ret = pair_verify_response2(vctx, in, in_len); + if (ret < 0) + break; + break; + default: + vctx->errmsg = "Verify: Unsupported state"; + ret = -1; + } + + if (ret < 0 || !(*out)) + return -1; + + return 0; +} + uint8_t * pair_verify_request1(size_t *len, struct pair_verify_context *vctx) { @@ -502,7 +640,7 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx) } int -pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) +pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len) { if (!vctx->type->pair_verify_response1) { @@ -510,11 +648,11 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz return -1; } - return vctx->type->pair_verify_response1(vctx, data, data_len); + return vctx->type->pair_verify_response1(vctx, in, in_len); } int -pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len) +pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len) { if (!vctx->type->pair_verify_response2) { @@ -522,28 +660,29 @@ pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, siz return -1; } - if (vctx->type->pair_verify_response2(vctx, data, data_len) != 0) + if (vctx->type->pair_verify_response2(vctx, in, in_len) != 0) return -1; - vctx->verify_is_completed = 1; return 0; } int -pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, struct pair_verify_context *vctx) +pair_verify_result(struct pair_result **result, struct pair_verify_context *vctx) { - if (!vctx->verify_is_completed) + if (vctx->status != PAIR_STATUS_COMPLETED) { vctx->errmsg = "Verify result: The pairing verification did not complete"; return -1; } - if (!vctx->type->pair_verify_result) - return -1; - - if (vctx->type->pair_verify_result(shared_secret, shared_secret_len, vctx) != 0) - return -1; + if (vctx->type->pair_verify_result) + { + if (vctx->type->pair_verify_result(vctx) != 0) + return -1; + } + if (result) + *result = &vctx->result; return 0; } @@ -575,7 +714,7 @@ pair_cipher_errmsg(struct pair_cipher_context *cctx) } ssize_t -pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx) +pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx) { if (!cctx->type->pair_encrypt) { @@ -587,7 +726,7 @@ pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, s } ssize_t -pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx) +pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, const uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx) { if (!cctx->type->pair_decrypt) { @@ -611,7 +750,40 @@ pair_decrypt_rollback(struct pair_cipher_context *cctx) } int -pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len) +pair_add(enum pair_type type, uint8_t **out, size_t *out_len, pair_cb add_cb, void *cb_arg, const uint8_t *in, size_t in_len) +{ + if (!pair[type]->pair_add) + { + return -1; + } + + return pair[type]->pair_add(out, out_len, add_cb, cb_arg, in, in_len); +} + +int +pair_remove(enum pair_type type, uint8_t **out, size_t *out_len, pair_cb remove_cb, void *cb_arg, const uint8_t *in, size_t in_len) +{ + if (!pair[type]->pair_remove) + { + return -1; + } + + return pair[type]->pair_remove(out, out_len, remove_cb, cb_arg, in, in_len); +} + +int +pair_list(enum pair_type type, uint8_t **out, size_t *out_len, pair_list_cb list_cb, void *cb_arg, const uint8_t *in, size_t in_len) +{ + if (!pair[type]->pair_list) + { + return -1; + } + + return pair[type]->pair_list(out, out_len, list_cb, cb_arg, in, in_len); +} + +int +pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *in, size_t in_len) { if (!pair[type]->pair_state_get) { @@ -619,5 +791,16 @@ pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, si return -1; } - return pair[type]->pair_state_get(errmsg, data, data_len); + return pair[type]->pair_state_get(errmsg, in, in_len); +} + +void +pair_public_key_get(enum pair_type type, uint8_t server_public_key[32], const char *device_id) +{ + if (!pair[type]->pair_public_key_get) + { + return; + } + + pair[type]->pair_public_key_get(server_public_key, device_id); } diff --git a/src/pair_ap/pair.h b/src/pair_ap/pair.h index b7b0f7a0..2e5b531b 100644 --- a/src/pair_ap/pair.h +++ b/src/pair_ap/pair.h @@ -4,7 +4,16 @@ #include #define PAIR_AP_VERSION_MAJOR 0 -#define PAIR_AP_VERSION_MINOR 2 +#define PAIR_AP_VERSION_MINOR 11 + +#define PAIR_AP_DEVICE_ID_LEN_MAX 64 + +#define PAIR_AP_POST_PIN_START "POST /pair-pin-start" +#define PAIR_AP_POST_SETUP "POST /pair-setup" +#define PAIR_AP_POST_VERIFY "POST /pair-verify" +#define PAIR_AP_POST_ADD "POST /pair-add" +#define PAIR_AP_POST_LIST "POST /pair-list" +#define PAIR_AP_POST_REMOVE "POST /pair-remove" enum pair_type { @@ -15,28 +24,80 @@ enum pair_type // verification PAIR_CLIENT_HOMEKIT_NORMAL, // Same as normal except PIN is fixed to 3939 and stops after setup step 2, - // when session key is established. This is the only mode where the server - // side is also supported. + // when session key is established PAIR_CLIENT_HOMEKIT_TRANSIENT, - PAIR_SERVER_HOMEKIT_TRANSIENT, + // Server side implementation supporting both transient and normal mode, + // letting client choose mode. If a PIN is with pair_setup_new() then only + // normal mode will be possible. + PAIR_SERVER_HOMEKIT, +}; + +/* This struct stores the various forms of pairing results. The shared secret + * is used to initialise an encrypted session via pair_cipher_new(). For + * non-transient client pair setup, you also get a key string (client_setup_keys) from + * pair_setup_result() that you can store and use to later initialise + * pair_verify_new(). For non-transient server pair setup, you can either: + * - Register an "add pairing" callback (add_cb) with pair_setup_new(), and + * then save the client id and key in the callback (see server-example.c for + * this approach). + * - Check pairing result with pair_setup_result() and if successful read and + * store the client id and key from the result struct. + * - Decide not to authenticate clients during pair-verify (set get_cb to NULL) + * in which case you don't need to save client ids and keys from pair-setup. + * + * Table showing returned data (everything else will be zeroed): + * + * | pair-setup | pair-verify + * --------------------------------|-------------------------------|-------------- + * PAIR_CLIENT_FRUIT | client keys | shared secret + * PAIR_CLIENT_HOMEKIT_NORMAL | client keys, server public | shared secret + | key, server id | shared secret + * PAIR_CLIENT_HOMEKIT_TRANSIENT | shared secret | n/a + * PAIR_SERVER_HOMEKIT (normal) | client public key, client id | shared secret + * PAIR_SERVER_HOMEKIT (transient) | shared secret | n/a + */ +struct pair_result +{ + char device_id[PAIR_AP_DEVICE_ID_LEN_MAX]; // ID of the peer + uint8_t client_private_key[64]; + uint8_t client_public_key[32]; + uint8_t server_public_key[32]; + uint8_t shared_secret[64]; + size_t shared_secret_len; // Will be 32 (normal) or 64 (transient) }; struct pair_setup_context; struct pair_verify_context; struct pair_cipher_context; +typedef int (*pair_cb)(uint8_t public_key[32], const char *device_id, void *cb_arg); +typedef void (*pair_list_cb)(pair_cb list_cb, void *list_cb_arg, void *cb_arg); + + +/* ------------------------------- pair setup ------------------------------- */ + /* 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. + * When you have the pin-code (must be 4 chars), create a new context with this + * function and then call pair_setup() or pair_setup_request1(). device_id is + * only required for Homekit pairing. If the client previously paired + * (non-transient) and has saved credentials, it should instead skip setup and + * only do verification. The callback is only for Homekit, and you can leave it + * at NULL if you don't care about saving ID and key of the server for later + * verification (then you also set get_cb to NULL in pair_verify_new), or if you + * will read the id and key via pair_setup_result. * * 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(). + * The client will make a connection and then at some point make a /pair-setup + * or a /pair-verify. The server should: + * - new /pair-setup: create a setup context with a pin-code (or NULL to allow + * transient pairing), and then call pair_setup() to process request and + * construct reply (also for subsequent /pair-setup requests) + * - new /pair_verify: create a verify context and then call pair_verify() + * to process request and construct reply (also for subsequent /pair-verify + * requests) */ struct pair_setup_context * -pair_setup_new(enum pair_type type, const char *pin, const char *device_id); +pair_setup_new(enum pair_type type, const char *pin, pair_cb add_cb, void *cb_arg, const char *device_id); void pair_setup_free(struct pair_setup_context *sctx); @@ -45,7 +106,25 @@ pair_setup_free(struct pair_setup_context *sctx); const char * pair_setup_errmsg(struct pair_setup_context *sctx); +/* Will create a request (if client) or response (if server) based on the setup + * context and last message from the peer. If this is the first client request + * then set *in to NULL. Returns negative on error. + */ +int +pair_setup(uint8_t **out, size_t *out_len, struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); +/* Returns the result of a pairing, or negative if pairing is not completed. See + * 'struct pair_result' for info about pairing results. The string is a + * representation of the result that is easy to persist and can be used to feed + * back into pair_verify_new. The result and string becomes invalid when you + * free sctx. + */ +int +pair_setup_result(const char **client_setup_keys, struct pair_result **result, struct pair_setup_context *sctx); + +/* These are for constructing specific message types and reading specific + * message types. Not needed for Homekit pairing if you use pair_setup(). + */ uint8_t * pair_setup_request1(size_t *len, struct pair_setup_context *sctx); uint8_t * @@ -54,28 +133,34 @@ uint8_t * pair_setup_request3(size_t *len, struct pair_setup_context *sctx); int -pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len); +pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); int -pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len); +pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); int -pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len); - -/* Returns a 0-terminated string that is the authorisation key, along with a - * pointer to the binary representation. The string can be used to initialize - * pair_verify_new(). - * Note that the pointers become invalid when you free sctx. - */ -int -pair_setup_result(const char **hexkey, const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx); +pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len); -/* When you have completed the setup you can extract a key with - * pair_setup_result(). Give the string as input to this function to - * create a verification context and then call pair_verify_request1() - * device_id is only required for homekit pairing, where it should have len 16. +/* ------------------------------ pair verify ------------------------------- */ + +/* Client + * When you have completed pair setup you get a string containing some keys + * from pair_setup_result(). Give the string as input to this function to create + * a verification context. Set the callback to NULL. Then call pair_verify(). + * The device_id is required for Homekit pairing. + * + * Server + * When you get a pair verify request from a new peer, create a new context with + * client_setup_keys set to NULL, with a callback set and the server's device ID + * (same as for setup). Then call pair_verify(). The callback is used to get + * the persisted client public key (saved after pair setup), so the client can + * be verified. You can set the callback to NULL if you don't care about that. + * If set, the callback is made as part of pair_verify_response2. The job of the + * callback is to fill out the public_key with the public key from the setup + * stage (see 'struct pair_result'). If the client device id is not known (i.e. + * it has not completed pair-setup), return -1. */ struct pair_verify_context * -pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id); +pair_verify_new(enum pair_type type, const char *client_setup_keys, pair_cb get_cb, void *cb_arg, const char *device_id); void pair_verify_free(struct pair_verify_context *vctx); @@ -84,25 +169,38 @@ pair_verify_free(struct pair_verify_context *vctx); const char * pair_verify_errmsg(struct pair_verify_context *vctx); +/* Will create a request (if client) or response (if server) based on the verify + * context and last message from the peer. If this is the first client request + * then set *in to NULL. Returns negative on error. + */ +int +pair_verify(uint8_t **out, size_t *out_len, struct pair_verify_context *sctx, const uint8_t *in, size_t in_len); + +/* Returns a pointer to the result of the pairing. Only the shared secret will + * be filled out. Note that the result become invalid when you free vctx. + */ +int +pair_verify_result(struct pair_result **result, struct pair_verify_context *vctx); + +/* These are for constructing specific message types and reading specific + * message types. Not needed for Homekit pairing where you can use pair_verify(). + */ 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); int -pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len); +pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len); int -pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len); - -/* Returns a pointer to the shared secret that is the result of the pairing. - * Note that the pointers become invalid when you free vctx. - */ -int -pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, struct pair_verify_context *vctx); +pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len); -/* 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 +/* ------------------------------- ciphering -------------------------------- */ + +/* When you have completed the verification you can extract a shared secret with + * pair_verify_result() - or, in case of transient pairing, from + * pair_setup_result(). Give the shared secret as input to this function to * create a ciphering context. */ struct pair_cipher_context * @@ -120,14 +218,14 @@ pair_cipher_errmsg(struct pair_cipher_context *cctx); * returned. */ ssize_t -pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx); +pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx); /* The return value equals length of ciphertext that was decrypted, so if the * return value == ciphertext_len then everything was decrypted. On error -1 is * returned. */ ssize_t -pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx); +pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, const uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx); /* Rolls back the nonce */ @@ -136,10 +234,33 @@ pair_encrypt_rollback(struct pair_cipher_context *cctx); void pair_decrypt_rollback(struct pair_cipher_context *cctx); + +/* --------------------------------- other ---------------------------------- */ + +/* These are for Homekit pairing where they are called by the controller, e.g. + * the Home app + * + * TODO this part is currenly not working + */ +int +pair_add(enum pair_type type, uint8_t **out, size_t *out_len, pair_cb add_cb, void *cb_arg, const uint8_t *in, size_t in_len); + +int +pair_remove(enum pair_type type, uint8_t **out, size_t *out_len, pair_cb remove_cb, void *cb_arg, const uint8_t *in, size_t in_len); + +int +pair_list(enum pair_type type, uint8_t **out, size_t *out_len, pair_list_cb list_cb, void *cb_arg, const uint8_t *in, size_t in_len); + /* 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); +pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *in, size_t in_len); + +/* For servers, pair_ap calculates the public key using device_id as a seed. + * This function returns that public key. + */ +void +pair_public_key_get(enum pair_type type, uint8_t server_public_key[32], const char *device_id); #endif /* !__PAIR_AP_H__ */ diff --git a/src/pair_ap/pair_fruit.c b/src/pair_ap/pair_fruit.c index c718e55e..86f23636 100644 --- a/src/pair_ap/pair_fruit.c +++ b/src/pair_ap/pair_fruit.c @@ -33,6 +33,8 @@ #include #include +#include + #include "pair-internal.h" /* ----------------------------- DEFINES ETC ------------------------------- */ @@ -58,6 +60,7 @@ typedef struct { bnum N; bnum g; + int N_len; } NGConstant; struct SRPUser @@ -120,6 +123,8 @@ new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex) bnum_hex2bn(ng->N, n_hex); bnum_hex2bn(ng->g, g_hex); + ng->N_len = bnum_num_bytes(ng->N); + return ng; } @@ -346,7 +351,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in bnum_bin2bn(s, bytes_s, len_s); bnum_bin2bn(B, bytes_B, len_B); - k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g); + k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g, usr->ng->N_len); bnum_new(v); bnum_new(tmp1); bnum_new(tmp2); @@ -355,7 +360,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in if (!s || !B || !k || !v || !tmp1 || !tmp2 || !tmp3) goto cleanup1; - u = H_nn_pad(usr->alg, usr->A, B); + u = H_nn_pad(usr->alg, usr->A, B, usr->ng->N_len); x = calculate_x(usr->alg, s, usr->username, usr->password, usr->password_len); if (!u || !x) goto cleanup2; @@ -595,11 +600,11 @@ encrypt_ctr(unsigned char *ciphertext, int ciphertext_len, /* -------------------------- IMPLEMENTATION -------------------------------- */ static int -pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id) +client_setup_new(struct pair_setup_context *handle, const char *pin, pair_cb add_cb, void *cb_arg, const char *device_id) { struct pair_client_setup_context *sctx = &handle->sctx.client; - if (sodium_init() == -1) + if (!is_initialized()) return -1; if (!pin || strlen(pin) < 4) @@ -611,7 +616,7 @@ pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const } static void -pair_client_setup_free(struct pair_setup_context *handle) +client_setup_free(struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; @@ -625,7 +630,7 @@ pair_client_setup_free(struct pair_setup_context *handle) } static uint8_t * -pair_client_setup_request1(size_t *len, struct pair_setup_context *handle) +client_setup_request1(size_t *len, struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; @@ -651,7 +656,7 @@ pair_client_setup_request1(size_t *len, struct pair_setup_context *handle) } static uint8_t * -pair_client_setup_request2(size_t *len, struct pair_setup_context *handle) +client_setup_request2(size_t *len, struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; @@ -681,7 +686,7 @@ pair_client_setup_request2(size_t *len, struct pair_setup_context *handle) } static uint8_t * -pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) +client_setup_request3(size_t *len, struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; plist_t dict; @@ -747,7 +752,7 @@ pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) } static int -pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; @@ -774,7 +779,7 @@ pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *da } static int -pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; @@ -806,7 +811,7 @@ pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *da } static int -pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; @@ -837,30 +842,36 @@ pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *da plist_free(dict); - handle->setup_is_completed = 1; + assert(sizeof(handle->result.client_private_key) == sizeof(sctx->private_key)); + assert(sizeof(handle->result.client_public_key) == sizeof(sctx->public_key)); + + memcpy(handle->result.client_private_key, sctx->private_key, sizeof(sctx->private_key)); + memcpy(handle->result.client_public_key, sctx->public_key, sizeof(sctx->public_key)); + + handle->status = PAIR_STATUS_COMPLETED; return 0; } static int -pair_client_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *handle) +client_setup_result(struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; + char *ptr; + int i; - // 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) - { - handle->errmsg = "Pair setup result: Unexpected keys, private key does not match public key"; - return -1; - } + // Last 32 bytes of the private key is the public key, so we don't need to + // explicitly export that + ptr = handle->result_str; + for (i = 0; i < sizeof(sctx->private_key); i++) + ptr += sprintf(ptr, "%02x", sctx->private_key[i]); // 2 x 64 bytes + *ptr = '\0'; - *key = sctx->private_key; - *key_len = sizeof(sctx->private_key); return 0; } static int -pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, const char *device_id) +client_verify_new(struct pair_verify_context *handle, const char *client_setup_keys, pair_cb cb, void *cb_arg, const char *device_id) { struct pair_client_verify_context *vctx = &handle->vctx.client; char hex[] = { 0, 0, 0 }; @@ -868,13 +879,13 @@ pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, c const char *ptr; int i; - if (sodium_init() == -1) + if (!is_initialized()) return -1; - if (!hexkey) + if (!client_setup_keys) return -1; - hexkey_len = strlen(hexkey); + hexkey_len = strlen(client_setup_keys); if (hexkey_len != 2 * sizeof(vctx->client_private_key)) return -1; @@ -885,7 +896,7 @@ pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, c if (device_id) memcpy(vctx->device_id, device_id, strlen(device_id)); - ptr = hexkey; + ptr = client_setup_keys; for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2) { hex[0] = ptr[0]; @@ -893,19 +904,13 @@ pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, c 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); - } + crypto_sign_ed25519_sk_to_pk(vctx->client_public_key, vctx->client_private_key); return 0; } static uint8_t * -pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) +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}; @@ -935,7 +940,7 @@ pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) } static uint8_t * -pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) +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]; @@ -983,14 +988,14 @@ pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) return NULL; } - ret = encrypt_ctr(encrypted, sizeof(encrypted), vctx->server_public_key, sizeof(vctx->server_public_key), signature, sizeof(signature), key, iv, &errmsg); + ret = encrypt_ctr(encrypted, sizeof(encrypted), vctx->server_fruit_public_key, sizeof(vctx->server_fruit_public_key), signature, sizeof(signature), key, iv, &errmsg); if (ret < 0) { handle->errmsg = errmsg; return NULL; } - *len = 4 + sizeof(vctx->server_public_key); + *len = 4 + sizeof(vctx->server_fruit_public_key); data = calloc(1, *len); if (!data) { @@ -998,18 +1003,18 @@ pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) return NULL; } - memcpy(data + 4, encrypted, sizeof(vctx->server_public_key)); + memcpy(data + 4, encrypted, sizeof(vctx->server_fruit_public_key)); return data; } static int -pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) +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); + wanted = sizeof(vctx->server_eph_public_key) + sizeof(vctx->server_fruit_public_key); if (data_len < wanted) { handle->errmsg = "Verify response 2: Unexpected response (too short)"; @@ -1017,25 +1022,21 @@ pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t * } memcpy(vctx->server_eph_public_key, data, sizeof(vctx->server_eph_public_key)); - memcpy(vctx->server_public_key, data + sizeof(vctx->server_eph_public_key), sizeof(vctx->server_public_key)); + memcpy(vctx->server_fruit_public_key, data + sizeof(vctx->server_eph_public_key), sizeof(vctx->server_fruit_public_key)); return 0; } static int -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) +client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) { struct pair_client_verify_context *vctx = &handle->vctx.client; + // TODO actually check response - *key = vctx->shared_secret; - *key_len = sizeof(vctx->shared_secret); + memcpy(handle->result.shared_secret, vctx->shared_secret, sizeof(vctx->shared_secret)); + handle->result.shared_secret_len = sizeof(vctx->shared_secret); + + handle->status = PAIR_STATUS_COMPLETED; return 0; } @@ -1043,24 +1044,23 @@ pair_client_verify_result(const uint8_t **key, size_t *key_len, struct pair_veri 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_new = client_setup_new, + .pair_setup_free = client_setup_free, + .pair_setup_result = 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_request1 = client_setup_request1, + .pair_setup_request2 = client_setup_request2, + .pair_setup_request3 = 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_setup_response1 = client_setup_response1, + .pair_setup_response2 = client_setup_response2, + .pair_setup_response3 = client_setup_response3, - .pair_verify_new = pair_client_verify_new, - .pair_verify_result = pair_client_verify_result, + .pair_verify_new = client_verify_new, - .pair_verify_request1 = pair_client_verify_request1, - .pair_verify_request2 = pair_client_verify_request2, + .pair_verify_request1 = client_verify_request1, + .pair_verify_request2 = client_verify_request2, - .pair_verify_response1 = pair_client_verify_response1, - .pair_verify_response2 = pair_client_verify_response2, + .pair_verify_response1 = client_verify_response1, + .pair_verify_response2 = client_verify_response2, }; diff --git a/src/pair_ap/pair_homekit.c b/src/pair_ap/pair_homekit.c index 81cc352d..3b303e0d 100644 --- a/src/pair_ap/pair_homekit.c +++ b/src/pair_ap/pair_homekit.c @@ -63,7 +63,8 @@ enum pair_keys PAIR_SETUP_MSG04, PAIR_SETUP_MSG05, PAIR_SETUP_MSG06, - PAIR_SETUP_SIGN, + PAIR_SETUP_CONTROLLER_SIGN, + PAIR_SETUP_ACCESSORY_SIGN, PAIR_VERIFY_MSG01, PAIR_VERIFY_MSG02, PAIR_VERIFY_MSG03, @@ -92,6 +93,7 @@ static struct pair_keys_map pair_keys_map[] = { 0x05, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg05" }, { 0x06, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg06" }, { 0, "Pair-Setup-Controller-Sign-Salt", "Pair-Setup-Controller-Sign-Info", "" }, + { 0, "Pair-Setup-Accessory-Sign-Salt", "Pair-Setup-Accessory-Sign-Info", "" }, // Used for /pair-verify { 0x01, NULL, NULL, "" }, @@ -124,7 +126,7 @@ enum pair_flags { // Forwards const struct pair_definition pair_client_homekit_normal; const struct pair_definition pair_client_homekit_transient; -const struct pair_definition pair_server_homekit_transient; +const struct pair_definition pair_server_homekit; /* ---------------------------------- SRP ----------------------------------- */ @@ -140,6 +142,7 @@ typedef struct { bnum N; bnum g; + int N_len; } NGConstant; struct SRPUser @@ -231,6 +234,8 @@ new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex) bnum_hex2bn(ng->N, n_hex); bnum_hex2bn(ng->g, g_hex); + ng->N_len = bnum_num_bytes(ng->N); + return ng; } @@ -404,8 +409,11 @@ static void srp_user_start_authentication(struct SRPUser *usr, const char **username, const unsigned char **bytes_A, int *len_A) { - bnum_random(usr->a, 256); // BN_hex2bn(&(usr->a), "D929DFB605687233C9E9030C2280156D03BDB9FDCF3CCE3BC27D9CCFCB5FF6A1"); + bnum_random(usr->a, 256); +#ifdef DEBUG_PAIR + bnum_dump("Random value of usr->a:\n", usr->a); +#endif bnum_modexp(usr->A, usr->ng->g, usr->a, usr->ng->N); @@ -441,7 +449,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in bnum_bin2bn(s, bytes_s, len_s); bnum_bin2bn(B, bytes_B, len_B); - k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g); + k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g, usr->ng->N_len); bnum_new(v); bnum_new(tmp1); @@ -451,7 +459,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in if (!s || !B || !k || !v || !tmp1 || !tmp2 || !tmp3) goto cleanup1; - u = H_nn_pad(usr->alg, usr->A, B); + u = H_nn_pad(usr->alg, usr->A, B, usr->ng->N_len); x = calculate_x(usr->alg, s, usr->username, usr->password, usr->password_len); if (!u || !x) goto cleanup2; @@ -533,6 +541,9 @@ srp_create_salted_verification_key(enum hash_alg alg, goto error; bnum_random(s, 128); // MODIFIED from csrp's BN_rand(s, 32, -1, 0) +#ifdef DEBUG_PAIR + bnum_dump("Random value of s:\n", s); +#endif x = calculate_x(alg, s, username, password, len_password); if (!x) @@ -597,8 +608,11 @@ srp_verifier_start_authentication(enum hash_alg alg, SRP_NGType ng_type, bnum_bin2bn(v, bytes_v, len_v); bnum_random(b, 256); // MODIFIED from BN_rand(b, 256, -1, 0) +#ifdef DEBUG_PAIR + bnum_dump("Random value of b:\n", b); +#endif - k = H_nn_pad(alg, ng->N, ng->g); // MODIFIED from H_nn(alg, ng->N, ng->g) + k = H_nn_pad(alg, ng->N, ng->g, ng->N_len); // MODIFIED from H_nn(alg, ng->N, ng->g) if (!k) goto error; @@ -706,8 +720,8 @@ srp_verifier_new(enum hash_alg alg, SRP_NGType ng_type, const char *username, if (bnum_is_zero(tmp1)) goto error; - k = H_nn_pad(alg, ng->N, ng->g); // MODIFIED from H_nn(alg, ng->N, ng->g) - u = H_nn_pad(alg, A, B); // MODIFIED from H_nn(alg, A, B) + k = H_nn_pad(alg, ng->N, ng->g, ng->N_len); // MODIFIED from H_nn(alg, ng->N, ng->g) + u = H_nn_pad(alg, A, B, ng->N_len); // MODIFIED from H_nn(alg, A, B) // S = (A *(v^u)) ^ b bnum_modexp(tmp1, v, u, ng->N); @@ -773,6 +787,20 @@ srp_verifier_get_session_key(struct SRPVerifier *ver, int *key_length) /* -------------------------------- HELPERS --------------------------------- */ +static void +hexread(uint8_t *out, size_t out_len, const char *in) +{ + char hex[] = { 0, 0, 0 }; + int i; + + for (i = 0; i < out_len; i++, in+=2) + { + hex[0] = in[0]; + hex[1] = in[1]; + out[i] = strtol(hex, NULL, 16); + } +} + static pair_tlv_values_t * message_process(const uint8_t *data, size_t data_len, const char **errmsg) { @@ -893,7 +921,7 @@ hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm } static int -encrypt_chacha(uint8_t *cipher, uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]) +encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]) { #ifdef CONFIG_OPENSSL EVP_CIPHER_CTX *ctx; @@ -908,7 +936,7 @@ encrypt_chacha(uint8_t *cipher, uint8_t *plain, size_t plain_len, const uint8_t if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary goto error; - if (EVP_EncryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) + if (ad_len > 0 && EVP_EncryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) goto error; if (EVP_EncryptUpdate(ctx, cipher, &len, plain, plain_len) != 1) @@ -940,7 +968,7 @@ encrypt_chacha(uint8_t *cipher, uint8_t *plain, size_t plain_len, const uint8_t if (gcry_cipher_setiv(hd, nonce, NONCE_LENGTH) != GPG_ERR_NO_ERROR) goto error; - if (gcry_cipher_authenticate(hd, ad, ad_len) != GPG_ERR_NO_ERROR) + if (ad_len > 0 && gcry_cipher_authenticate(hd, ad, ad_len) != GPG_ERR_NO_ERROR) goto error; if (gcry_cipher_encrypt(hd, cipher, plain_len, plain, plain_len) != GPG_ERR_NO_ERROR) @@ -961,7 +989,7 @@ encrypt_chacha(uint8_t *cipher, uint8_t *plain, size_t plain_len, const uint8_t } static int -decrypt_chacha(uint8_t *plain, uint8_t *cipher, size_t cipher_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]) +decrypt_chacha(uint8_t *plain, const uint8_t *cipher, size_t cipher_len, const uint8_t *key, size_t key_len, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len, const uint8_t nonce[NONCE_LENGTH]) { #ifdef CONFIG_OPENSSL EVP_CIPHER_CTX *ctx; @@ -979,14 +1007,12 @@ decrypt_chacha(uint8_t *plain, uint8_t *cipher, size_t cipher_len, const uint8_t if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1) goto error; - if (EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) + if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1) goto error; if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1) goto error; - assert(len == cipher_len); - if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1) goto error; @@ -1008,7 +1034,7 @@ decrypt_chacha(uint8_t *plain, uint8_t *cipher, size_t cipher_len, const uint8_t if (gcry_cipher_setiv(hd, nonce, NONCE_LENGTH) != GPG_ERR_NO_ERROR) goto error; - if (gcry_cipher_authenticate(hd, ad, ad_len) != GPG_ERR_NO_ERROR) + if (ad_len > 0 && gcry_cipher_authenticate(hd, ad, ad_len) != GPG_ERR_NO_ERROR) goto error; if (gcry_cipher_decrypt(hd, plain, cipher_len, cipher, cipher_len) != GPG_ERR_NO_ERROR) @@ -1029,46 +1055,101 @@ 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) +create_info(uint8_t *info, size_t *info_len, uint8_t *a, size_t a_len, uint8_t *b, size_t b_len, uint8_t *c, size_t c_len) +{ + if (a_len + b_len + c_len > *info_len) + return -1; + + *info_len = a_len + b_len + c_len; + memcpy(info, a, a_len); + memcpy(info + a_len, b, b_len); + memcpy(info + a_len + b_len, c, c_len); + + return 0; +} + +static int +create_and_sign_device_info(uint8_t *msg, size_t *msg_len, const char *device_id, uint8_t *device_x, size_t device_x_len, uint8_t *pk, size_t pk_len, uint8_t *sk) { pair_tlv_values_t *tlv; - uint8_t *device_info; - uint32_t device_info_len; + uint8_t device_info[256]; + size_t device_info_len; size_t device_id_len; uint8_t signature[crypto_sign_BYTES]; int ret; device_id_len = strlen(device_id); + device_info_len = sizeof(device_info); - device_info_len = device_pk_len + device_id_len + pk_len; - device_info = malloc(device_info_len); - - memcpy(device_info, device_pk, device_pk_len); - memcpy(device_info + device_pk_len, device_id, device_id_len); - memcpy(device_info + device_pk_len + device_id_len, pk, pk_len); + ret = create_info(device_info, &device_info_len, device_x, device_x_len, (uint8_t *)device_id, device_id_len, pk, pk_len); + if (ret < 0) + return -1; crypto_sign_detached(signature, NULL, device_info, device_info_len, sk); - free(device_info); 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 = pair_tlv_format(tlv, data, data_len); + ret = pair_tlv_format(tlv, msg, msg_len); pair_tlv_free(tlv); return ret; } +static int +create_and_sign_accessory_info(uint8_t *msg, size_t *msg_len, uint8_t *server_pk, size_t server_pk_len, const char *accessory_id, uint8_t *client_pk, size_t client_pk_len, uint8_t *sk) +{ + pair_tlv_values_t *tlv; + uint8_t accessory_info[256]; + size_t accessory_info_len; + size_t accessory_id_len; + uint8_t signature[crypto_sign_BYTES]; + int ret; + + accessory_id_len = strlen(accessory_id); + accessory_info_len = sizeof(accessory_info); + + ret = create_info(accessory_info, &accessory_info_len, server_pk, server_pk_len, (uint8_t *)accessory_id, accessory_id_len, client_pk, client_pk_len); + if (ret < 0) + return -1; + + crypto_sign_detached(signature, NULL, accessory_info, accessory_info_len, sk); + + tlv = pair_tlv_new(); + pair_tlv_add_value(tlv, TLVType_Identifier, (unsigned char *)accessory_id, accessory_id_len); + pair_tlv_add_value(tlv, TLVType_Signature, signature, sizeof(signature)); + + ret = pair_tlv_format(tlv, msg, msg_len); + + pair_tlv_free(tlv); + return ret; +} + +static int +verify_info(uint8_t *signature, uint8_t *pk, uint8_t *a, size_t a_len, uint8_t *b, size_t b_len, uint8_t *c, size_t c_len) +{ + uint8_t info[256]; + size_t info_len; + int ret; + + info_len = sizeof(info); + ret = create_info(info, &info_len, a, a_len, b, b_len, c, c_len); + if (ret < 0) + return -1; + + return crypto_sign_verify_detached(signature, info, info_len, pk); +} + /* ------------------------- CLIENT IMPLEMENTATION -------------------------- */ static int -pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id) +client_setup_new(struct pair_setup_context *handle, const char *pin, pair_cb add_cb, void *cb_arg, const char *device_id) { struct pair_client_setup_context *sctx = &handle->sctx.client; - if (sodium_init() == -1) + if (!is_initialized()) return -1; if (handle->type == &pair_client_homekit_normal) @@ -1081,19 +1162,29 @@ pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const pin = "3939"; } - if (device_id && strlen(device_id) != 16) + if (device_id && strlen(device_id) >= PAIR_AP_DEVICE_ID_LEN_MAX) return -1; memcpy(sctx->pin, pin, sizeof(sctx->pin)); + sctx->add_cb = add_cb; + sctx->add_cb_arg = cb_arg; + if (device_id) memcpy(sctx->device_id, device_id, strlen(device_id)); + crypto_sign_keypair(sctx->public_key, sctx->private_key); + +#ifdef DEBUG_PAIR + hexdump("Client public key:\n", sctx->public_key, sizeof(sctx->public_key)); + hexdump("Client private key:\n", sctx->private_key, sizeof(sctx->private_key)); +#endif + return 0; } static void -pair_client_setup_free(struct pair_setup_context *handle) +client_setup_free(struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; @@ -1107,7 +1198,7 @@ pair_client_setup_free(struct pair_setup_context *handle) } static uint8_t * -pair_client_setup_request1(size_t *len, struct pair_setup_context *handle) +client_setup_request1(size_t *len, struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; pair_tlv_values_t *request; @@ -1165,7 +1256,7 @@ pair_client_setup_request1(size_t *len, struct pair_setup_context *handle) } static uint8_t * -pair_client_setup_request2(size_t *len, struct pair_setup_context *handle) +client_setup_request2(size_t *len, struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; pair_tlv_values_t *request; @@ -1207,7 +1298,7 @@ pair_client_setup_request2(size_t *len, struct pair_setup_context *handle) } static uint8_t * -pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) +client_setup_request3(size_t *len, struct pair_setup_context *handle) { struct pair_client_setup_context *sctx = &handle->sctx.client; pair_tlv_values_t *request; @@ -1236,15 +1327,13 @@ pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) goto error; } - ret = hkdf_extract_expand(device_x, sizeof(device_x), session_key, session_key_len, PAIR_SETUP_SIGN); + ret = hkdf_extract_expand(device_x, sizeof(device_x), session_key, session_key_len, PAIR_SETUP_CONTROLLER_SIGN); if (ret < 0) { handle->errmsg = "Setup request 3: hkdf error getting device_x"; goto error; } - crypto_sign_keypair(sctx->public_key, sctx->private_key); - 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) { @@ -1252,7 +1341,7 @@ pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) goto error; } - ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, 64, PAIR_SETUP_MSG05); // TODO is session_key_len always 64? + ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, session_key_len, PAIR_SETUP_MSG05); if (ret < 0) { handle->errmsg = "Setup request 3: hkdf error getting derived_key"; @@ -1311,7 +1400,7 @@ pair_client_setup_request3(size_t *len, struct pair_setup_context *handle) } static int -pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; pair_tlv_values_t *response; @@ -1349,11 +1438,13 @@ pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *da } static int -pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; pair_tlv_values_t *response; pair_tlv_t *proof; + const uint8_t *session_key; + int session_key_len; response = message_process(data, data_len, &handle->errmsg); if (!response) @@ -1380,11 +1471,24 @@ pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *da goto error; } - pair_tlv_free(response); - if (handle->type == &pair_client_homekit_transient) - handle->setup_is_completed = 1; + { + session_key = srp_user_get_session_key(sctx->user, &session_key_len); + if (!session_key) + { + handle->errmsg = "Setup response 2: Could not compute session key"; + goto error; + } + assert(sizeof(handle->result.shared_secret) >= session_key_len); + + memcpy(handle->result.shared_secret, session_key, session_key_len); + handle->result.shared_secret_len = session_key_len; + + handle->status = PAIR_STATUS_COMPLETED; + } + + pair_tlv_free(response); return 0; error: @@ -1393,11 +1497,14 @@ pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *da } static int -pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; pair_tlv_values_t *response; pair_tlv_t *encrypted_data; + pair_tlv_t *device_id; + pair_tlv_t *pk; + pair_tlv_t *signature; uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; uint8_t derived_key[32]; @@ -1405,6 +1512,7 @@ pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *da uint8_t *decrypted_data = NULL; const uint8_t *session_key; int session_key_len; + uint8_t device_x[32]; int ret; response = message_process(data, data_len, &handle->errmsg); @@ -1427,7 +1535,7 @@ pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *da goto error; } - ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, 64, PAIR_SETUP_MSG06); // TODO is session_key_len always 64? + ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, session_key_len, PAIR_SETUP_MSG06); if (ret < 0) { handle->errmsg = "Setup response 3: hkdf error getting derived_key"; @@ -1461,12 +1569,45 @@ pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *da goto error; } - // TODO check identifier and signature - we get an identifier (36), a public key (32) and a signature (64) + ret = hkdf_extract_expand(device_x, sizeof(device_x), session_key, session_key_len, PAIR_SETUP_ACCESSORY_SIGN); + if (ret < 0) + { + handle->errmsg = "Setup response 3: hkdf error getting device_x"; + goto error; + } + + device_id = pair_tlv_get_value(response, TLVType_Identifier); + pk = pair_tlv_get_value(response, TLVType_PublicKey); + signature = pair_tlv_get_value(response, TLVType_Signature); + if (!device_id || device_id->size >= sizeof(handle->result.device_id) || !pk || pk->size != crypto_sign_PUBLICKEYBYTES || !signature || signature->size != crypto_sign_BYTES) + { + handle->errmsg = "Setup response 3: Missing/invalid device ID, public key or signature"; + goto error; + } + + ret = verify_info(signature->value, pk->value, device_x, sizeof(device_x), device_id->value, device_id->size, pk->value, pk->size); + if (ret < 0) + { + handle->errmsg = "Setup request 3: Invalid signature"; + goto error; + } + + assert(sizeof(handle->result.client_private_key) == sizeof(sctx->private_key)); + assert(sizeof(handle->result.client_public_key) == sizeof(sctx->public_key)); + assert(sizeof(handle->result.server_public_key) == pk->size); + + memcpy(handle->result.client_private_key, sctx->private_key, sizeof(sctx->private_key)); + memcpy(handle->result.client_public_key, sctx->public_key, sizeof(sctx->public_key)); + memcpy(handle->result.server_public_key, pk->value, pk->size); + memcpy(handle->result.device_id, device_id->value, device_id->size); + + if (sctx->add_cb) + sctx->add_cb(handle->result.server_public_key, handle->result.device_id, sctx->add_cb_arg); + + handle->status = PAIR_STATUS_COMPLETED; free(decrypted_data); pair_tlv_free(response); - - handle->setup_is_completed = 1; return 0; error: @@ -1476,85 +1617,67 @@ pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *da } static int -pair_client_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *handle) +client_setup_result(struct pair_setup_context *handle) { - struct pair_client_setup_context *sctx = &handle->sctx.client; - const uint8_t *session_key; - int session_key_len; + char *ptr; + int i; - 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) - { - 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 (handle->type == &pair_client_homekit_transient) - { - session_key = srp_user_get_session_key(sctx->user, &session_key_len); - *key = session_key; - *key_len = session_key_len; - return 0; - } + assert(sizeof(handle->result_str) >= 2 * sizeof(handle->result.client_private_key) + 2 * sizeof(handle->result.server_public_key) + 1); - return -1; + // It is enough to export the private key, since the public key can be + // extracted from that with crypto_sign_ed25519_sk_to_pk (it is the last 32 + // bytes) + ptr = handle->result_str; + for (i = 0; i < sizeof(handle->result.client_private_key); i++) + ptr += sprintf(ptr, "%02x", handle->result.client_private_key[i]); // 2 x 64 bytes + for (i = 0; i < sizeof(handle->result.server_public_key); i++) + ptr += sprintf(ptr, "%02x", handle->result.server_public_key[i]); // 2 x 32 bytes + *ptr = '\0'; + + return 0; } static int -pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, const char *device_id) +client_verify_new(struct pair_verify_context *handle, const char *client_setup_keys, pair_cb cb, void *cb_arg, 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) + if (!is_initialized()) return -1; - if (!hexkey) + if (!device_id || strlen(device_id) >= PAIR_AP_DEVICE_ID_LEN_MAX) return -1; - hexkey_len = strlen(hexkey); - - if (hexkey_len != 2 * sizeof(vctx->client_private_key)) + if (!client_setup_keys) 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) + hexkey_len = strlen(client_setup_keys); + if (hexkey_len == 2 * sizeof(vctx->client_private_key) + 2 * sizeof(vctx->server_public_key)) { - hex[0] = ptr[0]; - hex[1] = ptr[1]; - vctx->client_private_key[i] = strtol(hex, NULL, 16); + hexread(vctx->client_private_key, sizeof(vctx->client_private_key), client_setup_keys); + hexread(vctx->server_public_key, sizeof(vctx->server_public_key), client_setup_keys + 2 * sizeof(vctx->client_private_key)); + vctx->verify_server_signature = true; } - - ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key); - for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2) + else if (hexkey_len == 2 * sizeof(vctx->client_private_key)) // No server public key known, so signature validation will be skipped { - hex[0] = ptr[0]; - hex[1] = ptr[1]; - vctx->client_public_key[i] = strtol(hex, NULL, 16); + hexread(vctx->client_private_key, sizeof(vctx->client_private_key), client_setup_keys); } + else + return -1; + + crypto_sign_ed25519_sk_to_pk(vctx->client_public_key, vctx->client_private_key); + + snprintf(vctx->device_id, sizeof(vctx->device_id), "%s", device_id); return 0; } static uint8_t * -pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) +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}; +// const uint8_t basepoint[crypto_scalarmult_BYTES] = {9}; // 32 bytes pair_tlv_values_t *request; uint8_t *data; size_t data_len; @@ -1564,12 +1687,17 @@ pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) data = malloc(data_len); request = pair_tlv_new(); + crypto_box_keypair(vctx->client_eph_public_key, vctx->client_eph_private_key); + +/* + // TODO keep around in case box_keypair doesn't work ret = crypto_scalarmult(vctx->client_eph_public_key, vctx->client_eph_private_key, basepoint); if (ret < 0) { handle->errmsg = "Verify request 1: Curve 25519 returned an error"; goto error; } +*/ 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)); @@ -1593,7 +1721,7 @@ pair_client_verify_request1(size_t *len, struct pair_verify_context *handle) } static uint8_t * -pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) +client_verify_request2(size_t *len, struct pair_verify_context *handle) { struct pair_client_verify_context *vctx = &handle->vctx.client; pair_tlv_values_t *request; @@ -1664,12 +1792,14 @@ pair_client_verify_request2(size_t *len, struct pair_verify_context *handle) } static int -pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) +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; pair_tlv_values_t *response; pair_tlv_t *encrypted_data; pair_tlv_t *public_key; + pair_tlv_t *device_id; + pair_tlv_t *signature; uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; uint8_t derived_key[32]; @@ -1739,7 +1869,24 @@ pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t * goto error; } - // TODO check identifier and signature + device_id = pair_tlv_get_value(response, TLVType_Identifier); + signature = pair_tlv_get_value(response, TLVType_Signature); + if (!device_id || !signature || signature->size != crypto_sign_BYTES) + { + handle->errmsg = "Verify response 1: Missing device ID or signature"; + goto error; + } + + if (vctx->verify_server_signature) + { + ret = verify_info(signature->value, vctx->server_public_key, vctx->server_eph_public_key, sizeof(vctx->server_eph_public_key), + device_id->value, device_id->size, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key)); + if (ret < 0) + { + handle->errmsg = "Verify response 1: Invalid signature"; + goto error; + } + } free(decrypted_data); pair_tlv_free(response); @@ -1752,19 +1899,21 @@ pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t * } static int -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) +client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) { struct pair_client_verify_context *vctx = &handle->vctx.client; + pair_tlv_values_t *response; - *key = vctx->shared_secret; - *key_len = sizeof(vctx->shared_secret); + response = message_process(data, data_len, &handle->errmsg); + if (!response) + { + return -1; + } + + memcpy(handle->result.shared_secret, vctx->shared_secret, sizeof(vctx->shared_secret)); + handle->result.shared_secret_len = sizeof(vctx->shared_secret); + + handle->status = PAIR_STATUS_COMPLETED; return 0; } @@ -1772,34 +1921,75 @@ pair_client_verify_result(const uint8_t **key, size_t *key_len, struct pair_veri /* ------------------------- SERVER IMPLEMENTATION -------------------------- */ +// Use (unsecure) keys seeded from device_id. We need the keys to always be the +// same during pair setup and pair verify, since the client saves them after +// pair-setup 3, so that the signature in pair-verify 1 can be checked. +static void +server_keypair(uint8_t *public_key, uint8_t *private_key, const char *device_id) +{ + uint8_t seed[crypto_sign_SEEDBYTES] = { 0 }; + size_t len; + + len = strlen(device_id); + if (len > sizeof(seed)) + len = sizeof(seed); + + memcpy(seed, device_id, len); + + crypto_sign_seed_keypair(public_key, private_key, seed); +} + +static uint8_t * +server_auth_failed_response(size_t *len, enum pair_keys msg_state) +{ + pair_tlv_values_t *response; + uint8_t *data; + size_t data_len; + uint8_t error = TLVError_Authentication; + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[msg_state].state, sizeof(pair_keys_map[msg_state].state)); + pair_tlv_add_value(response, TLVType_Error, &error, sizeof(error)); + + pair_tlv_format(response, data, &data_len); + pair_tlv_free(response); + + *len = data_len; + + return data; +} + static int -pair_server_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id) +server_setup_new(struct pair_setup_context *handle, const char *pin, pair_cb add_cb, void *cb_arg, 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) + if (!is_initialized()) return -1; if (!pin) pin = "3939"; - if (device_id && strlen(device_id) != 16) + if (!device_id || strlen(device_id) >= PAIR_AP_DEVICE_ID_LEN_MAX) return -1; memcpy(sctx->pin, pin, sizeof(sctx->pin)); - if (device_id) - memcpy(sctx->device_id, device_id, strlen(device_id)); + sctx->add_cb = add_cb; + sctx->add_cb_arg = cb_arg; + + snprintf(sctx->device_id, sizeof(sctx->device_id), "%s", device_id); + + server_keypair(sctx->public_key, sctx->private_key, sctx->device_id); return 0; } static void -pair_server_setup_free(struct pair_setup_context *handle) +server_setup_free(struct pair_setup_context *handle) { struct pair_server_setup_context *sctx = &handle->sctx.server; @@ -1814,9 +2004,10 @@ pair_server_setup_free(struct pair_setup_context *handle) } static int -pair_server_setup_request1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; +// enum pair_keys msg_state = PAIR_SETUP_MSG01; pair_tlv_values_t *request; pair_tlv_t *method; pair_tlv_t *type; @@ -1825,38 +2016,31 @@ pair_server_setup_request1(struct pair_setup_context *handle, const uint8_t *dat request = message_process(data, data_len, &handle->errmsg); if (!request) { - goto error; + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); } 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; + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 1: Missing or unexpected pairing method in TLV"); } 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; - } + sctx->is_transient = (type && type->size == 1 && type->value[0] == PairingFlagsTransient); // 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; + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 1: Could not create verification key"); } 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; + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 1: Could not compute B"); } pair_tlv_free(request); @@ -1868,9 +2052,10 @@ pair_server_setup_request1(struct pair_setup_context *handle, const uint8_t *dat } static int -pair_server_setup_request2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +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; +// enum pair_keys msg_state = PAIR_SETUP_MSG03; pair_tlv_values_t *request; pair_tlv_t *pk; pair_tlv_t *proof; @@ -1878,15 +2063,14 @@ pair_server_setup_request2(struct pair_setup_context *handle, const uint8_t *dat request = message_process(data, data_len, &handle->errmsg); if (!request) { - goto error; + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); } 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; + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 2: Missing pkA or proof"); } sctx->pkA_len = pk->size; // 384 @@ -1901,19 +2085,642 @@ pair_server_setup_request2(struct pair_setup_context *handle, const uint8_t *dat 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; + handle->status = PAIR_STATUS_AUTH_FAILED; + goto out; } 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"; + handle->status = PAIR_STATUS_AUTH_FAILED; + goto out; // Not an error, server should give proper TLV-formatet reply + } + + out: + pair_tlv_free(request); + return 0; + + error: + pair_tlv_free(request); + return -1; +} + +static int +server_setup_request3(struct pair_setup_context *handle, const uint8_t *data, size_t data_len) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + enum pair_keys msg_state = PAIR_SETUP_MSG05; + pair_tlv_values_t *request; + pair_tlv_t *encrypted_data; + pair_tlv_t *device_id; + pair_tlv_t *pk; + pair_tlv_t *signature; + const uint8_t *session_key; + int session_key_len; + uint8_t nonce[NONCE_LENGTH] = { 0 }; + uint8_t tag[AUTHTAG_LENGTH]; + uint8_t derived_key[32]; + size_t encrypted_len; + uint8_t *decrypted_data = NULL; + uint8_t device_x[32]; + int ret; + + request = message_process(data, data_len, &handle->errmsg); + if (!request) + { + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); + } + + session_key = srp_verifier_get_session_key(sctx->verifier, &session_key_len); + if (!session_key) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: No valid session key"); + } + + ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, session_key_len, msg_state); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: hkdf error getting derived_key"); + } + + encrypted_data = pair_tlv_get_value(request, TLVType_EncryptedData); + if (!encrypted_data) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: Missing encrypted_data"); + } + + // encrypted_data->value consists of the encrypted payload + the auth tag + if (encrypted_data->size < AUTHTAG_LENGTH) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: Invalid encrypted data"); + } + + encrypted_len = encrypted_data->size - AUTHTAG_LENGTH; + memcpy(tag, encrypted_data->value + encrypted_len, AUTHTAG_LENGTH); + memcpy(nonce + 4, pair_keys_map[msg_state].nonce, NONCE_LENGTH - 4); + + decrypted_data = malloc(encrypted_len); + + ret = decrypt_chacha(decrypted_data, encrypted_data->value, encrypted_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: Decryption error"); + } + + pair_tlv_free(request); + request = message_process(decrypted_data, encrypted_len, &handle->errmsg); + if (!request) + { + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); + } + + ret = hkdf_extract_expand(device_x, sizeof(device_x), session_key, session_key_len, PAIR_SETUP_CONTROLLER_SIGN); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: hkdf error getting device_x"); + } + + device_id = pair_tlv_get_value(request, TLVType_Identifier); + pk = pair_tlv_get_value(request, TLVType_PublicKey); + signature = pair_tlv_get_value(request, TLVType_Signature); + if (!device_id || device_id->size >= sizeof(handle->result.device_id) || !pk || pk->size != crypto_sign_PUBLICKEYBYTES || !signature || signature->size != crypto_sign_BYTES) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 3: Missing/invalid device ID, public key or signature"); + } + + ret = verify_info(signature->value, pk->value, device_x, sizeof(device_x), device_id->value, device_id->size, pk->value, pk->size); + if (ret < 0) + { + handle->status = PAIR_STATUS_AUTH_FAILED; + goto out; + } + + memcpy(handle->result.device_id, device_id->value, device_id->size); + memcpy(handle->result.client_public_key, pk->value, pk->size); + + out: + free(decrypted_data); + pair_tlv_free(request); + return 0; + + error: + free(decrypted_data); + pair_tlv_free(request); + return -1; +} + +static uint8_t * +server_setup_response1(size_t *len, struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + enum pair_keys msg_state = PAIR_SETUP_MSG02; + pair_tlv_values_t *response; + uint8_t *data; + size_t data_len; + int ret; + + if (handle->status == PAIR_STATUS_AUTH_FAILED) + return server_auth_failed_response(len, msg_state); + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[msg_state].state, sizeof(pair_keys_map[msg_state].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) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 1: pair_tlv_format returned an error"); + } + + *len = data_len; + + pair_tlv_free(response); + return data; + + error: + free(data); + pair_tlv_free(response); + return NULL; +} + +static uint8_t * +server_setup_response2(size_t *len, struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + enum pair_keys msg_state = PAIR_SETUP_MSG04; + pair_tlv_values_t *response; + uint8_t *data; + size_t data_len; + const uint8_t *session_key; + int session_key_len; + int ret; + + if (handle->status == PAIR_STATUS_AUTH_FAILED) + return server_auth_failed_response(len, msg_state); + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[msg_state].state, sizeof(pair_keys_map[msg_state].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) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 2: pair_tlv_format returned an error"); + } + + if (sctx->is_transient) + { + session_key = srp_verifier_get_session_key(sctx->verifier, &session_key_len); + if (!session_key) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup request 2: Could not compute session key"); + } + + assert(sizeof(handle->result.shared_secret) >= session_key_len); + + memcpy(handle->result.shared_secret, session_key, session_key_len); + handle->result.shared_secret_len = session_key_len; + + handle->status = PAIR_STATUS_COMPLETED; + } + + *len = data_len; + + pair_tlv_free(response); + return data; + + error: + free(data); + pair_tlv_free(response); + return NULL; +} + +static uint8_t * +server_setup_response3(size_t *len, struct pair_setup_context *handle) +{ + struct pair_server_setup_context *sctx = &handle->sctx.server; + enum pair_keys msg_state = PAIR_SETUP_MSG06; + const uint8_t *session_key; + int session_key_len; + pair_tlv_values_t *response; + uint8_t nonce[NONCE_LENGTH] = { 0 }; + uint8_t tag[AUTHTAG_LENGTH]; + uint8_t derived_key[32]; + pair_tlv_values_t *append; + size_t append_len; + uint8_t *encrypted_data = NULL; + size_t encrypted_data_len; + uint8_t *data; + size_t data_len; + uint8_t device_x[32]; + int ret; + + if (handle->status == PAIR_STATUS_AUTH_FAILED) + return server_auth_failed_response(len, msg_state); + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + session_key = srp_verifier_get_session_key(sctx->verifier, &session_key_len); + if (!session_key) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: No valid session key"); + } + + ret = hkdf_extract_expand(device_x, sizeof(device_x), session_key, session_key_len, PAIR_SETUP_ACCESSORY_SIGN); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: hkdf error getting device_x"); + } + + 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) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: create device info returned an error"); + } + + // Append TLV-encoded public key to *data, which already has identifier and signature + append = pair_tlv_new(); + append_len = REQUEST_BUFSIZE - data_len; + 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) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: error appending public key to TLV"); + } + data_len += append_len; + + ret = hkdf_extract_expand(derived_key, sizeof(derived_key), session_key, session_key_len, msg_state); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: hkdf error getting derived_key"); + } + + memcpy(nonce + 4, pair_keys_map[msg_state].nonce, NONCE_LENGTH - 4); + + encrypted_data_len = data_len + sizeof(tag); // Space for ciphered payload and authtag + encrypted_data = malloc(encrypted_data_len); + + ret = encrypt_chacha(encrypted_data, data, data_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: Could not encrypt"); + } + + memcpy(encrypted_data + data_len, tag, sizeof(tag)); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[msg_state].state, sizeof(pair_keys_map[msg_state].state)); + pair_tlv_add_value(response, TLVType_EncryptedData, encrypted_data, encrypted_data_len); + + data_len = REQUEST_BUFSIZE; // Re-using *data, so pass original length to pair_tlv_format + ret = pair_tlv_format(response, data, &data_len); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Setup response 3: error appending public key to TLV"); + } + + if (sctx->add_cb) + sctx->add_cb(handle->result.client_public_key, handle->result.device_id, sctx->add_cb_arg); + + handle->status = PAIR_STATUS_COMPLETED; + + *len = data_len; + + free(encrypted_data); + pair_tlv_free(response); + return data; + + error: + free(encrypted_data); + free(data); + pair_tlv_free(response); + return NULL; +} + + +static int +server_verify_new(struct pair_verify_context *handle, const char *client_setup_keys, pair_cb cb, void *cb_arg, const char *device_id) +{ + struct pair_server_verify_context *vctx = &handle->vctx.server; + + if (!is_initialized()) + return -1; + + if (client_setup_keys) + return -1; + + if (!device_id || strlen(device_id) >= PAIR_AP_DEVICE_ID_LEN_MAX) + return -1; + + snprintf(vctx->device_id, sizeof(vctx->device_id), "%s", device_id); + + vctx->get_cb = cb; + vctx->get_cb_arg = cb_arg; + vctx->verify_client_signature = cb; + + server_keypair(vctx->server_public_key, vctx->server_private_key, vctx->device_id); + + return 0; +} + +static int +server_verify_request1(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) +{ + struct pair_server_verify_context *vctx = &handle->vctx.server; +// enum pair_keys msg_state = PAIR_VERIFY_MSG01; + pair_tlv_values_t *request; + pair_tlv_t *pk; + + request = message_process(data, data_len, &handle->errmsg); + if (!request) + { + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); + } + + pk = pair_tlv_get_value(request, TLVType_PublicKey); + if (!pk || pk->size != sizeof(vctx->client_eph_public_key)) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 1: Missing or invalid public_key"); + } + + memcpy(vctx->client_eph_public_key, pk->value, sizeof(vctx->client_eph_public_key)); + + pair_tlv_free(request); + return 0; + + error: + pair_tlv_free(request); + return -1; +} + +static int +server_verify_request2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len) +{ + struct pair_server_verify_context *vctx = &handle->vctx.server; + enum pair_keys msg_state = PAIR_VERIFY_MSG03; + pair_tlv_values_t *request; + pair_tlv_t *encrypted_data; + pair_tlv_t *device_id; + pair_tlv_t *signature; + uint8_t nonce[NONCE_LENGTH] = { 0 }; + uint8_t tag[AUTHTAG_LENGTH]; + uint8_t derived_key[32]; + size_t encrypted_len; + uint8_t *decrypted_data = NULL; + char id_str[PAIR_AP_DEVICE_ID_LEN_MAX] = { 0 }; + uint8_t client_public_key[crypto_sign_PUBLICKEYBYTES]; + int ret; + + request = message_process(data, data_len, &handle->errmsg); + if (!request) + { + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); + } + + ret = hkdf_extract_expand(derived_key, sizeof(derived_key), vctx->shared_secret, sizeof(vctx->shared_secret), msg_state); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 2: hkdf error getting derived_key"); + } + + encrypted_data = pair_tlv_get_value(request, TLVType_EncryptedData); + if (!encrypted_data) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 2: Missing encrypted_data"); + } + + // encrypted_data->value consists of the encrypted payload + the auth tag + if (encrypted_data->size < AUTHTAG_LENGTH) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 2: Invalid encrypted data"); + } + + encrypted_len = encrypted_data->size - AUTHTAG_LENGTH; + memcpy(tag, encrypted_data->value + encrypted_len, AUTHTAG_LENGTH); + memcpy(nonce + 4, pair_keys_map[msg_state].nonce, NONCE_LENGTH - 4); + + decrypted_data = malloc(encrypted_len); + + ret = decrypt_chacha(decrypted_data, encrypted_data->value, encrypted_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 2: Decryption error"); + } + + pair_tlv_free(request); + request = message_process(decrypted_data, encrypted_len, &handle->errmsg); + if (!request) + { + RETURN_ERROR(PAIR_STATUS_INVALID, handle->errmsg); + } + + device_id = pair_tlv_get_value(request, TLVType_Identifier); + signature = pair_tlv_get_value(request, TLVType_Signature); + if (!device_id || !signature || signature->size != crypto_sign_BYTES) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 2: Missing identifier or signature"); + } + + if (vctx->verify_client_signature) + { + if (device_id->size >= sizeof(id_str)) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify request 2: Device ID from peer is too long"); + } + + memcpy(id_str, device_id->value, device_id->size); + + ret = vctx->get_cb(client_public_key, id_str, vctx->get_cb_arg); + if (ret < 0) + { + handle->status = PAIR_STATUS_AUTH_FAILED; + goto out; + } + + ret = verify_info(signature->value, client_public_key, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key), + device_id->value, device_id->size, vctx->server_eph_public_key, sizeof(vctx->server_eph_public_key)); + if (ret < 0) + { + handle->status = PAIR_STATUS_AUTH_FAILED; + goto out; + } + } + + out: + free(decrypted_data); + pair_tlv_free(request); + return 0; + + error: + free(decrypted_data); + pair_tlv_free(request); + return -1; +} + +static uint8_t * +server_verify_response1(size_t *len, struct pair_verify_context *handle) +{ + struct pair_server_verify_context *vctx = &handle->vctx.server; + enum pair_keys msg_state = PAIR_VERIFY_MSG02; + pair_tlv_values_t *response; + uint8_t nonce[NONCE_LENGTH] = { 0 }; + uint8_t tag[AUTHTAG_LENGTH]; + uint8_t derived_key[32]; + uint8_t *encrypted_data = NULL; + size_t encrypted_data_len; + uint8_t *data; + size_t data_len; + int ret; + + if (handle->status == PAIR_STATUS_AUTH_FAILED) + return server_auth_failed_response(len, msg_state); + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + crypto_box_keypair(vctx->server_eph_public_key, vctx->server_eph_private_key); + + ret = crypto_scalarmult(vctx->shared_secret, vctx->server_eph_private_key, vctx->client_eph_public_key); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify response 1: Error generating shared secret"); + } + + ret = create_and_sign_accessory_info(data, &data_len, vctx->server_eph_public_key, sizeof(vctx->server_eph_public_key), vctx->device_id, + vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key), vctx->server_private_key); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify response 1: Error creating device info"); + } + + ret = hkdf_extract_expand(derived_key, sizeof(derived_key), vctx->shared_secret, sizeof(vctx->shared_secret), msg_state); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify response 1: hkdf error getting derived_key"); + } + + memcpy(nonce + 4, pair_keys_map[msg_state].nonce, NONCE_LENGTH - 4); + + encrypted_data_len = data_len + sizeof(tag); // Space for ciphered payload and authtag + encrypted_data = malloc(encrypted_data_len); + + ret = encrypt_chacha(encrypted_data, data, data_len, derived_key, sizeof(derived_key), NULL, 0, tag, sizeof(tag), nonce); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify response 1: Could not encrypt"); + } + + memcpy(encrypted_data + data_len, tag, sizeof(tag)); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[msg_state].state, sizeof(pair_keys_map[msg_state].state)); + pair_tlv_add_value(response, TLVType_PublicKey, vctx->server_eph_public_key, sizeof(vctx->server_eph_public_key)); + pair_tlv_add_value(response, TLVType_EncryptedData, encrypted_data, encrypted_data_len); + + data_len = REQUEST_BUFSIZE; // Re-using *data, so pass original length to pair_tlv_format + ret = pair_tlv_format(response, data, &data_len); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify response 1: pair_tlv_format returned an error"); + } + + *len = data_len; + + free(encrypted_data); + pair_tlv_free(response); + return data; + + error: + free(encrypted_data); + free(data); + pair_tlv_free(response); + return NULL; +} + +static uint8_t * +server_verify_response2(size_t *len, struct pair_verify_context *handle) +{ + struct pair_server_verify_context *vctx = &handle->vctx.server; + enum pair_keys msg_state = PAIR_VERIFY_MSG04; + pair_tlv_values_t *response; + uint8_t *data; + size_t data_len; + int ret; + + if (handle->status == PAIR_STATUS_AUTH_FAILED) + return server_auth_failed_response(len, msg_state); + + data_len = REQUEST_BUFSIZE; + data = malloc(data_len); + response = pair_tlv_new(); + + pair_tlv_add_value(response, TLVType_State, &pair_keys_map[msg_state].state, sizeof(pair_keys_map[msg_state].state)); + + ret = pair_tlv_format(response, data, &data_len); + if (ret < 0) + { + RETURN_ERROR(PAIR_STATUS_INVALID, "Verify response 2: pair_tlv_format returned an error"); + } + + *len = data_len; + + memcpy(handle->result.shared_secret, vctx->shared_secret, sizeof(vctx->shared_secret)); + handle->result.shared_secret_len = sizeof(vctx->shared_secret); + + handle->status = PAIR_STATUS_COMPLETED; + + pair_tlv_free(response); + return data; + + error: + free(data); + pair_tlv_free(response); + return NULL; +} + +static int +server_add_remove_request(pair_cb cb, void *cb_arg, const uint8_t *in, size_t in_len) +{ + const char *errmsg; + pair_tlv_values_t *request; + pair_tlv_t *device_id; + pair_tlv_t *pk; + char id_str[PAIR_AP_DEVICE_ID_LEN_MAX] = { 0 }; + uint8_t *public_key = NULL; + + request = message_process(in, in_len, &errmsg); + if (!request) + { goto error; } - handle->setup_is_completed = 1; + device_id = pair_tlv_get_value(request, TLVType_Identifier); + if (!device_id || device_id->size >= sizeof(id_str)) + { + goto error; + } + + // Only present when adding + pk = pair_tlv_get_value(request, TLVType_PublicKey); + if (pk && pk->size == crypto_sign_PUBLICKEYBYTES) + { + public_key = pk->value; + } + + memcpy(id_str, device_id->value, device_id->size); + + cb(public_key, id_str, cb_arg); + pair_tlv_free(request); return 0; @@ -1923,11 +2730,11 @@ pair_server_setup_request2(struct pair_setup_context *handle, const uint8_t *dat } static uint8_t * -pair_server_setup_response1(size_t *len, struct pair_setup_context *handle) +server_add_remove_response(size_t *len) { - struct pair_server_setup_context *sctx = &handle->sctx.server; pair_tlv_values_t *response; uint8_t *data; + uint8_t state = 2; size_t data_len; int ret; @@ -1935,14 +2742,11 @@ pair_server_setup_response1(size_t *len, struct pair_setup_context *handle) 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 + pair_tlv_add_value(response, TLVType_State, &state, sizeof(state)); ret = pair_tlv_format(response, data, &data_len); if (ret < 0) { - handle->errmsg = "Setup response 1: pair_tlv_format returned an error"; goto error; } @@ -1952,61 +2756,90 @@ pair_server_setup_response1(size_t *len, struct pair_setup_context *handle) 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) +server_add_remove(uint8_t **out, size_t *out_len, pair_cb cb, void *cb_arg, const uint8_t *in, size_t in_len) { - struct pair_server_setup_context *sctx = &handle->sctx.server; - const uint8_t *session_key; - int session_key_len; + int ret; - session_key = srp_verifier_get_session_key(sctx->verifier, &session_key_len); - if (!session_key) + ret = server_add_remove_request(cb, cb_arg, in, in_len); + if (ret < 0) + return -1; + + *out = server_add_remove_response(out_len); + if (!*out) + return -1; + + return 0; +} + +static int +server_list_cb(uint8_t public_key[crypto_sign_PUBLICKEYBYTES], const char *device_id, void *cb_arg) +{ + pair_tlv_values_t *response = cb_arg; + pair_tlv_t *previous_id; + uint8_t permissions = 1; // Means admin (TODO don't hardcode - let caller set) + + // If this isn't the first iteration (item) then we must add a separator + previous_id = pair_tlv_get_value(response, TLVType_Identifier); + if (previous_id) + pair_tlv_add_value(response, TLVType_Separator, NULL, 0); + + pair_tlv_add_value(response, TLVType_Identifier, (unsigned char *)device_id, strlen(device_id)); + pair_tlv_add_value(response, TLVType_PublicKey, public_key, crypto_sign_PUBLICKEYBYTES); + pair_tlv_add_value(response, TLVType_Permissions, &permissions, sizeof(permissions)); + + return 0; +} + +static uint8_t * +server_list_response(size_t *len, pair_list_cb cb, void *cb_arg) +{ + pair_tlv_values_t *response; + uint8_t *data; + uint8_t state = 2; + 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, &state, sizeof(state)); + + cb(server_list_cb, response, cb_arg); + + ret = pair_tlv_format(response, data, &data_len); + if (ret < 0) { - handle->errmsg = "Pair setup result: Could not compute session key"; - return -1; + goto error; } - *key = session_key; - *key_len = session_key_len; + *len = data_len; + + pair_tlv_free(response); + return data; + + error: + free(data); + pair_tlv_free(response); + return NULL; +} + +static int +server_list(uint8_t **out, size_t *out_len, pair_list_cb cb, void *cb_arg, const uint8_t *in, size_t in_len) +{ + // Skip reading the request, it just has state = 1 and pair method = + // PairingMethodListPairings + + *out = server_list_response(out_len, cb, cb_arg); + if (!*out) + return -1; + return 0; } @@ -2014,7 +2847,7 @@ pair_server_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup /* ----------------------- CIPHERING IMPLEMENTATION ------------------------- */ static void -pair_cipher_free(struct pair_cipher_context *cctx) +cipher_free(struct pair_cipher_context *cctx) { if (!cctx) return; @@ -2023,7 +2856,7 @@ pair_cipher_free(struct pair_cipher_context *cctx) } static struct pair_cipher_context * -pair_cipher_new(struct pair_definition *type, int channel, const uint8_t *shared_secret, size_t shared_secret_len) +cipher_new(struct pair_definition *type, int channel, const uint8_t *shared_secret, size_t shared_secret_len) { struct pair_cipher_context *cctx; enum pair_keys write_key; @@ -2075,11 +2908,11 @@ pair_cipher_new(struct pair_definition *type, int channel, const uint8_t *shared } static ssize_t -pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx) +encrypt(uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx) { uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; - uint8_t *plain_block; + const uint8_t *plain_block; uint8_t *cipher_block; uint16_t block_len; int nblocks; @@ -2131,12 +2964,12 @@ pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, s } static ssize_t -pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx) +decrypt(uint8_t **plaintext, size_t *plaintext_len, const uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx) { uint8_t nonce[NONCE_LENGTH] = { 0 }; uint8_t tag[AUTHTAG_LENGTH]; uint8_t *plain_block; - uint8_t *cipher_block; + const uint8_t *cipher_block; uint16_t block_len; int ret; @@ -2185,12 +3018,17 @@ pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, si } static int -pair_state_get(const char **errmsg, const uint8_t *data, size_t data_len) +state_get(const char **errmsg, const uint8_t *data, size_t data_len) { pair_tlv_values_t *message; pair_tlv_t *state; int ret; + if (!data || data_len == 0) + { + return 0; // state 0 = no incoming data yet -> first request + } + message = message_process(data, data_len, errmsg); if (!message) { @@ -2214,87 +3052,105 @@ pair_state_get(const char **errmsg, const uint8_t *data, size_t data_len) return -1; } +static void +public_key_get(uint8_t server_public_key[crypto_sign_PUBLICKEYBYTES], const char *device_id) +{ + uint8_t private_key[crypto_sign_SECRETKEYBYTES]; + server_keypair(server_public_key, private_key, device_id); +} + 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_new = client_setup_new, + .pair_setup_free = client_setup_free, + .pair_setup_result = 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_request1 = client_setup_request1, + .pair_setup_request2 = client_setup_request2, + .pair_setup_request3 = 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_setup_response1 = client_setup_response1, + .pair_setup_response2 = client_setup_response2, + .pair_setup_response3 = client_setup_response3, - .pair_verify_new = pair_client_verify_new, - .pair_verify_result = pair_client_verify_result, + .pair_verify_new = client_verify_new, - .pair_verify_request1 = pair_client_verify_request1, - .pair_verify_request2 = pair_client_verify_request2, + .pair_verify_request1 = client_verify_request1, + .pair_verify_request2 = client_verify_request2, - .pair_verify_response1 = pair_client_verify_response1, - .pair_verify_response2 = pair_client_verify_response2, + .pair_verify_response1 = client_verify_response1, + .pair_verify_response2 = client_verify_response2, - .pair_cipher_new = pair_cipher_new, - .pair_cipher_free = pair_cipher_free, + .pair_cipher_new = cipher_new, + .pair_cipher_free = cipher_free, - .pair_encrypt = pair_encrypt, - .pair_decrypt = pair_decrypt, + .pair_encrypt = encrypt, + .pair_decrypt = decrypt, - .pair_state_get = pair_state_get, + .pair_state_get = state_get, }; const struct pair_definition pair_client_homekit_transient = { - .pair_setup_new = pair_client_setup_new, - .pair_setup_free = pair_client_setup_free, - .pair_setup_result = pair_client_setup_result, + .pair_setup_new = client_setup_new, + .pair_setup_free = client_setup_free, - .pair_setup_request1 = pair_client_setup_request1, - .pair_setup_request2 = pair_client_setup_request2, - .pair_setup_request3 = pair_client_setup_request3, + .pair_setup_request1 = client_setup_request1, + .pair_setup_request2 = client_setup_request2, + .pair_setup_request3 = 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_setup_response1 = client_setup_response1, + .pair_setup_response2 = client_setup_response2, + .pair_setup_response3 = client_setup_response3, - .pair_verify_new = pair_client_verify_new, - .pair_verify_result = pair_client_verify_result, + .pair_verify_new = client_verify_new, - .pair_verify_request1 = pair_client_verify_request1, - .pair_verify_request2 = pair_client_verify_request2, + .pair_verify_request1 = client_verify_request1, + .pair_verify_request2 = client_verify_request2, - .pair_verify_response1 = pair_client_verify_response1, - .pair_verify_response2 = pair_client_verify_response2, + .pair_verify_response1 = client_verify_response1, + .pair_verify_response2 = client_verify_response2, - .pair_cipher_new = pair_cipher_new, - .pair_cipher_free = pair_cipher_free, + .pair_cipher_new = cipher_new, + .pair_cipher_free = cipher_free, - .pair_encrypt = pair_encrypt, - .pair_decrypt = pair_decrypt, + .pair_encrypt = encrypt, + .pair_decrypt = decrypt, - .pair_state_get = pair_state_get, + .pair_state_get = state_get, }; -const struct pair_definition pair_server_homekit_transient = +const struct pair_definition pair_server_homekit = { - .pair_setup_new = pair_server_setup_new, - .pair_setup_free = pair_server_setup_free, - .pair_setup_result = pair_server_setup_result, + .pair_setup_new = server_setup_new, + .pair_setup_free = server_setup_free, - .pair_setup_request1 = pair_server_setup_response1, - .pair_setup_request2 = pair_server_setup_response2, + .pair_setup_request1 = server_setup_response1, + .pair_setup_request2 = server_setup_response2, + .pair_setup_request3 = server_setup_response3, - .pair_setup_response1 = pair_server_setup_request1, - .pair_setup_response2 = pair_server_setup_request2, + .pair_setup_response1 = server_setup_request1, + .pair_setup_response2 = server_setup_request2, + .pair_setup_response3 = server_setup_request3, - .pair_cipher_new = pair_cipher_new, - .pair_cipher_free = pair_cipher_free, + .pair_verify_new = server_verify_new, - .pair_encrypt = pair_encrypt, - .pair_decrypt = pair_decrypt, + .pair_verify_request1 = server_verify_response1, + .pair_verify_request2 = server_verify_response2, - .pair_state_get = pair_state_get, + .pair_verify_response1 = server_verify_request1, + .pair_verify_response2 = server_verify_request2, + + .pair_add = server_add_remove, + .pair_remove = server_add_remove, + .pair_list = server_list, + + .pair_cipher_new = cipher_new, + .pair_cipher_free = cipher_free, + + .pair_encrypt = encrypt, + .pair_decrypt = decrypt, + + .pair_state_get = state_get, + .pair_public_key_get = public_key_get, };