mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
[airplay] Update pairing modules (pair_ap 0.2)
This commit is contained in:
parent
6b3573d4f5
commit
bbe7526414
@ -21,7 +21,9 @@ if COND_MPD
|
|||||||
MPD_SRC=mpd.c mpd.h
|
MPD_SRC=mpd.c mpd.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
PAIR_AP_SRC=outputs/pair_fruit.c outputs/pair_homekit.c outputs/pair.c outputs/pair-internal.h outputs/pair.h
|
PAIR_AP_SRC = \
|
||||||
|
pair_ap/pair_fruit.c pair_ap/pair_homekit.c pair_ap/pair-tlv.c \
|
||||||
|
pair_ap/pair.c pair_ap/pair-tlv.h pair_ap/pair-internal.h pair_ap/pair.h
|
||||||
AM_CPPFLAGS += -DCONFIG_GCRYPT
|
AM_CPPFLAGS += -DCONFIG_GCRYPT
|
||||||
|
|
||||||
if COND_ALSA
|
if COND_ALSA
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
#include "transcode.h"
|
#include "transcode.h"
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
|
|
||||||
#include "pair.h"
|
#include "pair_ap/pair.h"
|
||||||
|
|
||||||
/* List of TODO's for AirPlay 2
|
/* List of TODO's for AirPlay 2
|
||||||
*
|
*
|
||||||
@ -3025,9 +3025,9 @@ payload_make_pair_generic(int step, struct evrtsp_request *req, struct airplay_s
|
|||||||
free(body);
|
free(body);
|
||||||
|
|
||||||
// Required!!
|
// Required!!
|
||||||
if (rs->pair_type == PAIR_HOMEKIT_NORMAL)
|
if (rs->pair_type == PAIR_CLIENT_HOMEKIT_NORMAL)
|
||||||
evrtsp_add_header(req->output_headers, "X-Apple-HKP", "3");
|
evrtsp_add_header(req->output_headers, "X-Apple-HKP", "3");
|
||||||
else if (rs->pair_type == PAIR_HOMEKIT_TRANSIENT)
|
else if (rs->pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT)
|
||||||
evrtsp_add_header(req->output_headers, "X-Apple-HKP", "4");
|
evrtsp_add_header(req->output_headers, "X-Apple-HKP", "4");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -3040,7 +3040,7 @@ payload_make_pair_setup1(struct evrtsp_request *req, struct airplay_session *rs,
|
|||||||
char device_id_hex[16 + 1];
|
char device_id_hex[16 + 1];
|
||||||
|
|
||||||
if (pin)
|
if (pin)
|
||||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||||
|
|
||||||
snprintf(device_id_hex, sizeof(device_id_hex), "%016" PRIX64, airplay_device_id);
|
snprintf(device_id_hex, sizeof(device_id_hex), "%016" PRIX64, airplay_device_id);
|
||||||
|
|
||||||
@ -3420,7 +3420,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session
|
|||||||
// Evaluate what next sequence based on response
|
// Evaluate what next sequence based on response
|
||||||
if (rs->statusflags & AIRPLAY_FLAG_ONE_TIME_PAIRING_REQUIRED)
|
if (rs->statusflags & AIRPLAY_FLAG_ONE_TIME_PAIRING_REQUIRED)
|
||||||
{
|
{
|
||||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||||
|
|
||||||
if (!device->auth_key)
|
if (!device->auth_key)
|
||||||
{
|
{
|
||||||
@ -3438,7 +3438,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session
|
|||||||
device->auth_key = NULL;
|
device->auth_key = NULL;
|
||||||
device->requires_auth = 1;
|
device->requires_auth = 1;
|
||||||
|
|
||||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||||
rs->state = AIRPLAY_STATE_AUTH;
|
rs->state = AIRPLAY_STATE_AUTH;
|
||||||
return AIRPLAY_SEQ_PIN_START;
|
return AIRPLAY_SEQ_PIN_START;
|
||||||
}
|
}
|
||||||
@ -3449,7 +3449,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session
|
|||||||
return AIRPLAY_SEQ_ABORT;
|
return AIRPLAY_SEQ_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
rs->pair_type = PAIR_HOMEKIT_TRANSIENT;
|
rs->pair_type = PAIR_CLIENT_HOMEKIT_TRANSIENT;
|
||||||
rs->state = AIRPLAY_STATE_INFO;
|
rs->state = AIRPLAY_STATE_INFO;
|
||||||
return AIRPLAY_SEQ_PAIR_TRANSIENT;
|
return AIRPLAY_SEQ_PAIR_TRANSIENT;
|
||||||
}
|
}
|
||||||
@ -3535,14 +3535,14 @@ response_handler_pair_setup1(struct evrtsp_request *req, struct airplay_session
|
|||||||
{
|
{
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
|
|
||||||
if (rs->pair_type == PAIR_HOMEKIT_TRANSIENT && req->response_code == RTSP_CONNECTION_AUTH_REQUIRED)
|
if (rs->pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT && req->response_code == RTSP_CONNECTION_AUTH_REQUIRED)
|
||||||
{
|
{
|
||||||
device = outputs_device_get(rs->device_id);
|
device = outputs_device_get(rs->device_id);
|
||||||
if (!device)
|
if (!device)
|
||||||
return AIRPLAY_SEQ_ABORT;
|
return AIRPLAY_SEQ_ABORT;
|
||||||
|
|
||||||
device->requires_auth = 1; // FIXME might be reset by mdns announcement
|
device->requires_auth = 1; // FIXME might be reset by mdns announcement
|
||||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||||
|
|
||||||
return AIRPLAY_SEQ_PIN_START;
|
return AIRPLAY_SEQ_PIN_START;
|
||||||
}
|
}
|
||||||
@ -3562,7 +3562,7 @@ response_handler_pair_setup2(struct evrtsp_request *req, struct airplay_session
|
|||||||
if (seq_type != AIRPLAY_SEQ_CONTINUE)
|
if (seq_type != AIRPLAY_SEQ_CONTINUE)
|
||||||
return seq_type;
|
return seq_type;
|
||||||
|
|
||||||
if (rs->pair_type != PAIR_HOMEKIT_TRANSIENT)
|
if (rs->pair_type != PAIR_CLIENT_HOMEKIT_TRANSIENT)
|
||||||
return seq_type;
|
return seq_type;
|
||||||
|
|
||||||
ret = pair_setup_result(NULL, &shared_secret, &shared_secret_len, rs->pair_setup_ctx);
|
ret = pair_setup_result(NULL, &shared_secret, &shared_secret_len, rs->pair_setup_ctx);
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
#include "dmap_common.h"
|
#include "dmap_common.h"
|
||||||
#include "rtp_common.h"
|
#include "rtp_common.h"
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
#include "pair.h"
|
#include "pair_ap/pair.h"
|
||||||
|
|
||||||
#define ALAC_HEADER_LEN 3
|
#define ALAC_HEADER_LEN 3
|
||||||
|
|
||||||
@ -4198,7 +4198,7 @@ raop_pair_verify(struct raop_session *rs)
|
|||||||
if (!device)
|
if (!device)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_FRUIT, device->auth_key, NULL));
|
CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_CLIENT_FRUIT, device->auth_key, NULL));
|
||||||
|
|
||||||
ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1);
|
ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -4307,7 +4307,7 @@ raop_pair_setup(struct raop_session *rs, const char *pin)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
rs->pair_setup_ctx = pair_setup_new(PAIR_FRUIT, pin, NULL);
|
rs->pair_setup_ctx = pair_setup_new(PAIR_CLIENT_FRUIT, pin, NULL);
|
||||||
if (!rs->pair_setup_ctx)
|
if (!rs->pair_setup_ctx)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n");
|
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n");
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
struct SRPUser;
|
struct SRPUser;
|
||||||
|
struct SRPVerifier;
|
||||||
|
|
||||||
struct pair_setup_context
|
struct pair_client_setup_context
|
||||||
{
|
{
|
||||||
struct pair_definition *type;
|
|
||||||
|
|
||||||
struct SRPUser *user;
|
struct SRPUser *user;
|
||||||
|
|
||||||
char pin[4];
|
char pin[4];
|
||||||
@ -28,23 +27,62 @@ struct pair_setup_context
|
|||||||
uint64_t salt_len;
|
uint64_t salt_len;
|
||||||
uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
|
uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
|
||||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
// Hex-formatet concatenation of public + private, 0-terminated
|
|
||||||
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
|
|
||||||
|
|
||||||
// We don't actually use the server's epk and authtag for anything
|
// We don't actually use the server's epk and authtag for anything
|
||||||
uint8_t *epk;
|
uint8_t *epk;
|
||||||
uint64_t epk_len;
|
uint64_t epk_len;
|
||||||
uint8_t *authtag;
|
uint8_t *authtag;
|
||||||
uint64_t authtag_len;
|
uint64_t authtag_len;
|
||||||
|
|
||||||
int setup_is_completed;
|
|
||||||
const char *errmsg;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pair_verify_context
|
struct pair_server_setup_context
|
||||||
|
{
|
||||||
|
struct SRPVerifier *verifier;
|
||||||
|
|
||||||
|
char pin[4];
|
||||||
|
char device_id[17]; // Incl. zero term
|
||||||
|
|
||||||
|
uint8_t *pkA;
|
||||||
|
uint64_t pkA_len;
|
||||||
|
|
||||||
|
uint8_t *pkB;
|
||||||
|
int pkB_len;
|
||||||
|
|
||||||
|
uint8_t *b;
|
||||||
|
int b_len;
|
||||||
|
|
||||||
|
uint8_t *M1;
|
||||||
|
uint64_t M1_len;
|
||||||
|
|
||||||
|
const uint8_t *M2;
|
||||||
|
int M2_len;
|
||||||
|
|
||||||
|
uint8_t *v;
|
||||||
|
int v_len;
|
||||||
|
|
||||||
|
uint8_t *salt;
|
||||||
|
int salt_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_setup_context
|
||||||
{
|
{
|
||||||
struct pair_definition *type;
|
struct pair_definition *type;
|
||||||
|
|
||||||
|
int setup_is_completed;
|
||||||
|
const char *errmsg;
|
||||||
|
|
||||||
|
// Hex-formatet concatenation of public + private, 0-terminated
|
||||||
|
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
|
||||||
|
|
||||||
|
union pair_setup_union
|
||||||
|
{
|
||||||
|
struct pair_client_setup_context client;
|
||||||
|
struct pair_server_setup_context server;
|
||||||
|
} sctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_client_verify_context
|
||||||
|
{
|
||||||
char device_id[17]; // Incl. zero term
|
char device_id[17]; // Incl. zero term
|
||||||
|
|
||||||
uint8_t server_eph_public_key[32];
|
uint8_t server_eph_public_key[32];
|
||||||
@ -57,9 +95,19 @@ struct pair_verify_context
|
|||||||
uint8_t client_eph_private_key[32];
|
uint8_t client_eph_private_key[32];
|
||||||
|
|
||||||
uint8_t shared_secret[32];
|
uint8_t shared_secret[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_verify_context
|
||||||
|
{
|
||||||
|
struct pair_definition *type;
|
||||||
|
|
||||||
int verify_is_completed;
|
int verify_is_completed;
|
||||||
const char *errmsg;
|
const char *errmsg;
|
||||||
|
|
||||||
|
union pair_verify_union
|
||||||
|
{
|
||||||
|
struct pair_client_verify_context client;
|
||||||
|
} vctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pair_cipher_context
|
struct pair_cipher_context
|
||||||
@ -81,7 +129,7 @@ struct pair_cipher_context
|
|||||||
|
|
||||||
struct pair_definition
|
struct pair_definition
|
||||||
{
|
{
|
||||||
struct pair_setup_context *(*pair_setup_new)(struct pair_definition *type, const char *pin, const char *device_id);
|
int (*pair_setup_new)(struct pair_setup_context *sctx, const char *pin, const char *device_id);
|
||||||
void (*pair_setup_free)(struct pair_setup_context *sctx);
|
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)(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx);
|
||||||
|
|
||||||
@ -93,6 +141,10 @@ struct pair_definition
|
|||||||
int (*pair_setup_response2)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
int (*pair_setup_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_response3)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||||
|
|
||||||
|
int (*pair_verify_new)(struct pair_verify_context *vctx, const char *hexkey, const char *device_id);
|
||||||
|
void (*pair_verify_free)(struct pair_verify_context *vctx);
|
||||||
|
int (*pair_verify_result)(const uint8_t **key, size_t *key_len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
uint8_t *(*pair_verify_request1)(size_t *len, struct pair_verify_context *vctx);
|
uint8_t *(*pair_verify_request1)(size_t *len, struct pair_verify_context *vctx);
|
||||||
uint8_t *(*pair_verify_request2)(size_t *len, struct pair_verify_context *vctx);
|
uint8_t *(*pair_verify_request2)(size_t *len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
@ -104,6 +156,8 @@ struct pair_definition
|
|||||||
|
|
||||||
ssize_t (*pair_encrypt)(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
|
ssize_t (*pair_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_decrypt)(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
int (*pair_state_get)(const char **errmsg, const uint8_t *data, size_t data_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -133,11 +187,16 @@ struct pair_definition
|
|||||||
#define bnum_add(bn, a, b) gcry_mpi_add(bn, a, b)
|
#define bnum_add(bn, a, b) gcry_mpi_add(bn, a, b)
|
||||||
#define bnum_sub(bn, a, b) gcry_mpi_sub(bn, a, b)
|
#define bnum_sub(bn, a, b) gcry_mpi_sub(bn, a, b)
|
||||||
#define bnum_mul(bn, a, b) gcry_mpi_mul(bn, a, b)
|
#define bnum_mul(bn, a, b) gcry_mpi_mul(bn, a, b)
|
||||||
|
#define bnum_mod(bn, a, b) gcry_mpi_mod(bn, a, b)
|
||||||
typedef gcry_mpi_t bnum;
|
typedef gcry_mpi_t bnum;
|
||||||
__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
||||||
{
|
{
|
||||||
gcry_mpi_powm(bn, y, q, p);
|
gcry_mpi_powm(bn, y, q, p);
|
||||||
}
|
}
|
||||||
|
__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m)
|
||||||
|
{
|
||||||
|
gcry_mpi_addm(bn, a, b, m);
|
||||||
|
}
|
||||||
#elif CONFIG_OPENSSL
|
#elif CONFIG_OPENSSL
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
#include <openssl/bn.h>
|
#include <openssl/bn.h>
|
||||||
@ -162,6 +221,13 @@ __attribute__((unused)) static void bnum_mul(bnum bn, bnum a, bnum b)
|
|||||||
BN_mul(bn, a, b, ctx);
|
BN_mul(bn, a, b, ctx);
|
||||||
BN_CTX_free(ctx);
|
BN_CTX_free(ctx);
|
||||||
}
|
}
|
||||||
|
__attribute__((unused)) static void bnum_mod(bnum bn, bnum a, bnum b)
|
||||||
|
{
|
||||||
|
// No error handling
|
||||||
|
BN_CTX *ctx = BN_CTX_new();
|
||||||
|
BN_mod(bn, a, b, ctx);
|
||||||
|
BN_CTX_free(ctx);
|
||||||
|
}
|
||||||
__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
||||||
{
|
{
|
||||||
// No error handling
|
// No error handling
|
||||||
@ -169,6 +235,13 @@ __attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
|||||||
BN_mod_exp(bn, y, q, p, ctx);
|
BN_mod_exp(bn, y, q, p, ctx);
|
||||||
BN_CTX_free(ctx);
|
BN_CTX_free(ctx);
|
||||||
}
|
}
|
||||||
|
__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m)
|
||||||
|
{
|
||||||
|
// No error handling
|
||||||
|
BN_CTX *ctx = BN_CTX_new();
|
||||||
|
BN_mod_add(bn, a, b, m, ctx);
|
||||||
|
BN_CTX_free(ctx);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
218
src/pair_ap/pair-tlv.c
Normal file
218
src/pair_ap/pair-tlv.c
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
* TLV helpers are adapted from ESP homekit:
|
||||||
|
* <https://github.com/maximkulkin/esp-homekit>
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
* so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include "pair-tlv.h"
|
||||||
|
#include "pair-internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
tlv_add_value_(pair_tlv_values_t *values, uint8_t type, uint8_t *value, size_t size) {
|
||||||
|
pair_tlv_t *tlv = malloc(sizeof(pair_tlv_t));
|
||||||
|
if (!tlv) {
|
||||||
|
return PAIR_TLV_ERROR_MEMORY;
|
||||||
|
}
|
||||||
|
tlv->type = type;
|
||||||
|
tlv->size = size;
|
||||||
|
tlv->value = value;
|
||||||
|
tlv->next = NULL;
|
||||||
|
|
||||||
|
if (!values->head) {
|
||||||
|
values->head = tlv;
|
||||||
|
} else {
|
||||||
|
pair_tlv_t *t = values->head;
|
||||||
|
while (t->next) {
|
||||||
|
t = t->next;
|
||||||
|
}
|
||||||
|
t->next = tlv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pair_tlv_values_t *
|
||||||
|
pair_tlv_new() {
|
||||||
|
pair_tlv_values_t *values = malloc(sizeof(pair_tlv_values_t));
|
||||||
|
if (!values)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
values->head = NULL;
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pair_tlv_free(pair_tlv_values_t *values) {
|
||||||
|
pair_tlv_t *t = values->head;
|
||||||
|
while (t) {
|
||||||
|
pair_tlv_t *t2 = t;
|
||||||
|
t = t->next;
|
||||||
|
if (t2->value)
|
||||||
|
free(t2->value);
|
||||||
|
free(t2);
|
||||||
|
}
|
||||||
|
free(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size) {
|
||||||
|
uint8_t *data = NULL;
|
||||||
|
int ret;
|
||||||
|
if (size) {
|
||||||
|
data = malloc(size);
|
||||||
|
if (!data) {
|
||||||
|
return PAIR_TLV_ERROR_MEMORY;
|
||||||
|
}
|
||||||
|
memcpy(data, value, size);
|
||||||
|
}
|
||||||
|
ret = tlv_add_value_(values, type, data, size);
|
||||||
|
if (ret < 0)
|
||||||
|
free(data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair_tlv_t *
|
||||||
|
pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type) {
|
||||||
|
pair_tlv_t *t = values->head;
|
||||||
|
while (t) {
|
||||||
|
if (t->type == type)
|
||||||
|
return t;
|
||||||
|
t = t->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size) {
|
||||||
|
size_t required_size = 0;
|
||||||
|
pair_tlv_t *t = values->head;
|
||||||
|
while (t) {
|
||||||
|
required_size += t->size + 2 * ((t->size + 254) / 255);
|
||||||
|
t = t->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*size < required_size) {
|
||||||
|
*size = required_size;
|
||||||
|
return PAIR_TLV_ERROR_INSUFFICIENT_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = required_size;
|
||||||
|
|
||||||
|
t = values->head;
|
||||||
|
while (t) {
|
||||||
|
uint8_t *data = t->value;
|
||||||
|
if (!t->size) {
|
||||||
|
buffer[0] = t->type;
|
||||||
|
buffer[1] = 0;
|
||||||
|
buffer += 2;
|
||||||
|
t = t->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t remaining = t->size;
|
||||||
|
|
||||||
|
while (remaining) {
|
||||||
|
buffer[0] = t->type;
|
||||||
|
size_t chunk_size = (remaining > 255) ? 255 : remaining;
|
||||||
|
buffer[1] = chunk_size;
|
||||||
|
memcpy(&buffer[2], data, chunk_size);
|
||||||
|
remaining -= chunk_size;
|
||||||
|
buffer += chunk_size + 2;
|
||||||
|
data += chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values) {
|
||||||
|
size_t i = 0;
|
||||||
|
int ret;
|
||||||
|
while (i < length) {
|
||||||
|
uint8_t type = buffer[i];
|
||||||
|
size_t size = 0;
|
||||||
|
uint8_t *data = NULL;
|
||||||
|
|
||||||
|
// scan TLVs to accumulate total size of subsequent TLVs with same type (chunked data)
|
||||||
|
size_t j = i;
|
||||||
|
while (j < length && buffer[j] == type && buffer[j+1] == 255) {
|
||||||
|
size_t chunk_size = buffer[j+1];
|
||||||
|
size += chunk_size;
|
||||||
|
j += chunk_size + 2;
|
||||||
|
}
|
||||||
|
if (j < length && buffer[j] == type) {
|
||||||
|
size_t chunk_size = buffer[j+1];
|
||||||
|
size += chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate memory to hold all pieces of chunked data and copy data there
|
||||||
|
if (size != 0) {
|
||||||
|
data = malloc(size);
|
||||||
|
if (!data)
|
||||||
|
return PAIR_TLV_ERROR_MEMORY;
|
||||||
|
|
||||||
|
uint8_t *p = data;
|
||||||
|
|
||||||
|
size_t remaining = size;
|
||||||
|
while (remaining) {
|
||||||
|
size_t chunk_size = buffer[i+1];
|
||||||
|
memcpy(p, &buffer[i+2], chunk_size);
|
||||||
|
p += chunk_size;
|
||||||
|
i += chunk_size + 2;
|
||||||
|
remaining -= chunk_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = tlv_add_value_(values, type, data, size);
|
||||||
|
if (ret < 0) {
|
||||||
|
free(data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_PAIR
|
||||||
|
void
|
||||||
|
pair_tlv_debug(const pair_tlv_values_t *values)
|
||||||
|
{
|
||||||
|
printf("Received TLV values\n");
|
||||||
|
for (pair_tlv_t *t=values->head; t; t=t->next)
|
||||||
|
{
|
||||||
|
printf("Type %d value (%zu bytes): \n", t->type, t->size);
|
||||||
|
hexdump("", t->value, t->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
77
src/pair_ap/pair-tlv.h
Normal file
77
src/pair_ap/pair-tlv.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef __PAIR_AP_TLV_H__
|
||||||
|
#define __PAIR_AP_TLV_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PAIR_TLV_ERROR_MEMORY -1
|
||||||
|
#define PAIR_TLV_ERROR_INSUFFICIENT_SIZE -2
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TLVType_Method = 0, // (integer) Method to use for pairing. See PairMethod
|
||||||
|
TLVType_Identifier = 1, // (UTF-8) Identifier for authentication
|
||||||
|
TLVType_Salt = 2, // (bytes) 16+ bytes of random salt
|
||||||
|
TLVType_PublicKey = 3, // (bytes) Curve25519, SRP public key or signed Ed25519 key
|
||||||
|
TLVType_Proof = 4, // (bytes) Ed25519 or SRP proof
|
||||||
|
TLVType_EncryptedData = 5, // (bytes) Encrypted data with auth tag at end
|
||||||
|
TLVType_State = 6, // (integer) State of the pairing process. 1=M1, 2=M2, etc.
|
||||||
|
TLVType_Error = 7, // (integer) Error code. Must only be present if error code is
|
||||||
|
// not 0. See TLVError
|
||||||
|
TLVType_RetryDelay = 8, // (integer) Seconds to delay until retrying a setup code
|
||||||
|
TLVType_Certificate = 9, // (bytes) X.509 Certificate
|
||||||
|
TLVType_Signature = 10, // (bytes) Ed25519
|
||||||
|
TLVType_Permissions = 11, // (integer) Bit value describing permissions of the controller
|
||||||
|
// being added.
|
||||||
|
// None (0x00): Regular user
|
||||||
|
// Bit 1 (0x01): Admin that is able to add and remove
|
||||||
|
// pairings against the accessory
|
||||||
|
TLVType_FragmentData = 13, // (bytes) Non-last fragment of data. If length is 0,
|
||||||
|
// it's an ACK.
|
||||||
|
TLVType_FragmentLast = 14, // (bytes) Last fragment of data
|
||||||
|
TLVType_Flags = 19, // Added from airplay2_receiver
|
||||||
|
TLVType_Separator = 0xff,
|
||||||
|
} TLVType;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TLVError_Unknown = 1, // Generic error to handle unexpected errors
|
||||||
|
TLVError_Authentication = 2, // Setup code or signature verification failed
|
||||||
|
TLVError_Backoff = 3, // Client must look at the retry delay TLV item and
|
||||||
|
// wait that many seconds before retrying
|
||||||
|
TLVError_MaxPeers = 4, // Server cannot accept any more pairings
|
||||||
|
TLVError_MaxTries = 5, // Server reached its maximum number of
|
||||||
|
// authentication attempts
|
||||||
|
TLVError_Unavailable = 6, // Server pairing method is unavailable
|
||||||
|
TLVError_Busy = 7, // Server is busy and cannot accept a pairing
|
||||||
|
// request at this time
|
||||||
|
} TLVError;
|
||||||
|
|
||||||
|
typedef struct _tlv {
|
||||||
|
struct _tlv *next;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t *value;
|
||||||
|
size_t size;
|
||||||
|
} pair_tlv_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pair_tlv_t *head;
|
||||||
|
} pair_tlv_values_t;
|
||||||
|
|
||||||
|
|
||||||
|
pair_tlv_values_t *pair_tlv_new();
|
||||||
|
|
||||||
|
void pair_tlv_free(pair_tlv_values_t *values);
|
||||||
|
|
||||||
|
int pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size);
|
||||||
|
|
||||||
|
pair_tlv_t *pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type);
|
||||||
|
|
||||||
|
int pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size);
|
||||||
|
|
||||||
|
int pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values);
|
||||||
|
|
||||||
|
#ifdef DEBUG_PAIR
|
||||||
|
void pair_tlv_debug(const pair_tlv_values_t *values);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !__PAIR_AP_TLV_H__ */
|
@ -31,15 +31,17 @@
|
|||||||
#include "pair.h"
|
#include "pair.h"
|
||||||
#include "pair-internal.h"
|
#include "pair-internal.h"
|
||||||
|
|
||||||
extern struct pair_definition pair_fruit;
|
extern struct pair_definition pair_client_fruit;
|
||||||
extern struct pair_definition pair_homekit_normal;
|
extern struct pair_definition pair_client_homekit_normal;
|
||||||
extern struct pair_definition pair_homekit_transient;
|
extern struct pair_definition pair_client_homekit_transient;
|
||||||
|
extern struct pair_definition pair_server_homekit_transient;
|
||||||
|
|
||||||
// Must be in sync with enum pair_type
|
// Must be in sync with enum pair_type
|
||||||
static struct pair_definition *pair[] = {
|
static struct pair_definition *pair[] = {
|
||||||
&pair_fruit,
|
&pair_client_fruit,
|
||||||
&pair_homekit_normal,
|
&pair_client_homekit_normal,
|
||||||
&pair_homekit_transient,
|
&pair_client_homekit_transient,
|
||||||
|
&pair_server_homekit_transient,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -------------------------- SHARED HASHING HELPERS ------------------------ */
|
/* -------------------------- SHARED HASHING HELPERS ------------------------ */
|
||||||
@ -274,10 +276,24 @@ hexdump(const char *msg, uint8_t *mem, size_t len)
|
|||||||
struct pair_setup_context *
|
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, const char *device_id)
|
||||||
{
|
{
|
||||||
|
struct pair_setup_context *sctx;
|
||||||
|
|
||||||
if (!pair[type]->pair_setup_new)
|
if (!pair[type]->pair_setup_new)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return pair[type]->pair_setup_new(pair[type], pin, device_id);
|
sctx = calloc(1, sizeof(struct pair_setup_context));
|
||||||
|
if (!sctx)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sctx->type = pair[type];
|
||||||
|
|
||||||
|
if (pair[type]->pair_setup_new(sctx, pin, device_id) < 0)
|
||||||
|
{
|
||||||
|
free(sctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -286,10 +302,10 @@ pair_setup_free(struct pair_setup_context *sctx)
|
|||||||
if (!sctx)
|
if (!sctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!sctx->type->pair_setup_free)
|
if (sctx->type->pair_setup_free)
|
||||||
return;
|
sctx->type->pair_setup_free(sctx);
|
||||||
|
|
||||||
return sctx->type->pair_setup_free(sctx);
|
free(sctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
@ -302,12 +318,10 @@ uint8_t *
|
|||||||
pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
|
pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
if (!sctx->type->pair_setup_request1)
|
if (!sctx->type->pair_setup_request1)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup request 1: Unsupported";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
return sctx->type->pair_setup_request1(len, sctx);
|
|
||||||
|
|
||||||
if (!sctx->type->pair_setup_request1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return sctx->type->pair_setup_request1(len, sctx);
|
return sctx->type->pair_setup_request1(len, sctx);
|
||||||
}
|
}
|
||||||
@ -316,7 +330,10 @@ uint8_t *
|
|||||||
pair_setup_request2(size_t *len, struct pair_setup_context *sctx)
|
pair_setup_request2(size_t *len, struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
if (!sctx->type->pair_setup_request2)
|
if (!sctx->type->pair_setup_request2)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup request 2: Unsupported";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return sctx->type->pair_setup_request2(len, sctx);
|
return sctx->type->pair_setup_request2(len, sctx);
|
||||||
}
|
}
|
||||||
@ -325,7 +342,10 @@ uint8_t *
|
|||||||
pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
if (!sctx->type->pair_setup_request3)
|
if (!sctx->type->pair_setup_request3)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup request 3: Unsupported";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return sctx->type->pair_setup_request3(len, sctx);
|
return sctx->type->pair_setup_request3(len, sctx);
|
||||||
}
|
}
|
||||||
@ -334,7 +354,10 @@ int
|
|||||||
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
if (!sctx->type->pair_setup_response1)
|
if (!sctx->type->pair_setup_response1)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup response 1: Unsupported";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return sctx->type->pair_setup_response1(sctx, data, data_len);
|
return sctx->type->pair_setup_response1(sctx, data, data_len);
|
||||||
}
|
}
|
||||||
@ -343,7 +366,10 @@ int
|
|||||||
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
if (!sctx->type->pair_setup_response2)
|
if (!sctx->type->pair_setup_response2)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup response 2: Unsupported";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return sctx->type->pair_setup_response2(sctx, data, data_len);
|
return sctx->type->pair_setup_response2(sctx, data, data_len);
|
||||||
}
|
}
|
||||||
@ -352,7 +378,10 @@ int
|
|||||||
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
if (!sctx->type->pair_setup_response3)
|
if (!sctx->type->pair_setup_response3)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup response 3: Unsupported";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (sctx->type->pair_setup_response3(sctx, data, data_len) != 0)
|
if (sctx->type->pair_setup_response3(sctx, data, data_len) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -375,13 +404,21 @@ pair_setup_result(const char **hexkey, const uint8_t **key, size_t *key_len, str
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sctx->type->pair_setup_result)
|
if (!sctx->type->pair_setup_result)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup result: Unsupported";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (sctx->type->pair_setup_result(&out_key, &out_len, sctx) != 0)
|
if (sctx->type->pair_setup_result(&out_key, &out_len, sctx) != 0)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (2 * out_len + 1 > sizeof(sctx->auth_key))
|
if (2 * out_len + 1 > sizeof(sctx->auth_key))
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup result: Invalid key length";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ptr = sctx->auth_key;
|
ptr = sctx->auth_key;
|
||||||
for (i = 0; i < out_len; i++)
|
for (i = 0; i < out_len; i++)
|
||||||
@ -403,23 +440,8 @@ struct pair_verify_context *
|
|||||||
pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id)
|
pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id)
|
||||||
{
|
{
|
||||||
struct pair_verify_context *vctx;
|
struct pair_verify_context *vctx;
|
||||||
char hex[] = { 0, 0, 0 };
|
|
||||||
size_t hexkey_len;
|
|
||||||
const char *ptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
if (!pair[type]->pair_verify_new)
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!hexkey)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
hexkey_len = strlen(hexkey);
|
|
||||||
|
|
||||||
if (hexkey_len != 2 * sizeof(vctx->client_private_key))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (device_id && strlen(device_id) != 16)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
vctx = calloc(1, sizeof(struct pair_verify_context));
|
vctx = calloc(1, sizeof(struct pair_verify_context));
|
||||||
@ -428,23 +450,10 @@ pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id)
|
|||||||
|
|
||||||
vctx->type = pair[type];
|
vctx->type = pair[type];
|
||||||
|
|
||||||
if (device_id)
|
if (pair[type]->pair_verify_new(vctx, hexkey, device_id) < 0)
|
||||||
memcpy(vctx->device_id, device_id, strlen(device_id));
|
|
||||||
|
|
||||||
ptr = hexkey;
|
|
||||||
for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2)
|
|
||||||
{
|
{
|
||||||
hex[0] = ptr[0];
|
free(vctx);
|
||||||
hex[1] = ptr[1];
|
return NULL;
|
||||||
vctx->client_private_key[i] = strtol(hex, NULL, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key);
|
|
||||||
for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2)
|
|
||||||
{
|
|
||||||
hex[0] = ptr[0];
|
|
||||||
hex[1] = ptr[1];
|
|
||||||
vctx->client_public_key[i] = strtol(hex, NULL, 16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return vctx;
|
return vctx;
|
||||||
@ -456,6 +465,9 @@ pair_verify_free(struct pair_verify_context *vctx)
|
|||||||
if (!vctx)
|
if (!vctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (vctx->type->pair_verify_free)
|
||||||
|
vctx->type->pair_verify_free(vctx);
|
||||||
|
|
||||||
free(vctx);
|
free(vctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,7 +481,10 @@ uint8_t *
|
|||||||
pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
||||||
{
|
{
|
||||||
if (!vctx->type->pair_verify_request1)
|
if (!vctx->type->pair_verify_request1)
|
||||||
|
{
|
||||||
|
vctx->errmsg = "Verify request 1: Unsupported";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return vctx->type->pair_verify_request1(len, vctx);
|
return vctx->type->pair_verify_request1(len, vctx);
|
||||||
}
|
}
|
||||||
@ -478,7 +493,10 @@ uint8_t *
|
|||||||
pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
||||||
{
|
{
|
||||||
if (!vctx->type->pair_verify_request2)
|
if (!vctx->type->pair_verify_request2)
|
||||||
|
{
|
||||||
|
vctx->errmsg = "Verify request 2: Unsupported";
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return vctx->type->pair_verify_request2(len, vctx);
|
return vctx->type->pair_verify_request2(len, vctx);
|
||||||
}
|
}
|
||||||
@ -487,7 +505,10 @@ int
|
|||||||
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
if (!vctx->type->pair_verify_response1)
|
if (!vctx->type->pair_verify_response1)
|
||||||
|
{
|
||||||
|
vctx->errmsg = "Verify response 1: Unsupported";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return vctx->type->pair_verify_response1(vctx, data, data_len);
|
return vctx->type->pair_verify_response1(vctx, data, data_len);
|
||||||
}
|
}
|
||||||
@ -496,7 +517,10 @@ int
|
|||||||
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
if (!vctx->type->pair_verify_response2)
|
if (!vctx->type->pair_verify_response2)
|
||||||
|
{
|
||||||
|
vctx->errmsg = "Verify response 2: Unsupported";
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (vctx->type->pair_verify_response2(vctx, data, data_len) != 0)
|
if (vctx->type->pair_verify_response2(vctx, data, data_len) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -514,8 +538,11 @@ pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, str
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*shared_secret = vctx->shared_secret;
|
if (!vctx->type->pair_verify_result)
|
||||||
*shared_secret_len = sizeof(vctx->shared_secret);
|
return -1;
|
||||||
|
|
||||||
|
if (vctx->type->pair_verify_result(shared_secret, shared_secret_len, vctx) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -551,7 +578,10 @@ ssize_t
|
|||||||
pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx)
|
pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx)
|
||||||
{
|
{
|
||||||
if (!cctx->type->pair_encrypt)
|
if (!cctx->type->pair_encrypt)
|
||||||
return 0;
|
{
|
||||||
|
cctx->errmsg = "Encryption unsupported";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return cctx->type->pair_encrypt(ciphertext, ciphertext_len, plaintext, plaintext_len, cctx);
|
return cctx->type->pair_encrypt(ciphertext, ciphertext_len, plaintext, plaintext_len, cctx);
|
||||||
}
|
}
|
||||||
@ -560,7 +590,10 @@ ssize_t
|
|||||||
pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx)
|
pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx)
|
||||||
{
|
{
|
||||||
if (!cctx->type->pair_decrypt)
|
if (!cctx->type->pair_decrypt)
|
||||||
return 0;
|
{
|
||||||
|
cctx->errmsg = "Decryption unsupported";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return cctx->type->pair_decrypt(plaintext, plaintext_len, ciphertext, ciphertext_len, cctx);
|
return cctx->type->pair_decrypt(plaintext, plaintext_len, ciphertext, ciphertext_len, cctx);
|
||||||
}
|
}
|
||||||
@ -576,3 +609,15 @@ pair_decrypt_rollback(struct pair_cipher_context *cctx)
|
|||||||
{
|
{
|
||||||
cctx->decryption_counter = cctx->decryption_counter_prev;
|
cctx->decryption_counter = cctx->decryption_counter_prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len)
|
||||||
|
{
|
||||||
|
if (!pair[type]->pair_state_get)
|
||||||
|
{
|
||||||
|
*errmsg = "Getting pair state unsupported";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pair[type]->pair_state_get(errmsg, data, data_len);
|
||||||
|
}
|
@ -3,26 +3,37 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PAIR_AP_VERSION_MAJOR 0
|
||||||
|
#define PAIR_AP_VERSION_MINOR 2
|
||||||
|
|
||||||
enum pair_type
|
enum pair_type
|
||||||
{
|
{
|
||||||
// This is the pairing type required for Apple TV device verification, which
|
// This is the pairing type required for Apple TV device verification, which
|
||||||
// became mandatory with tvOS 10.2.
|
// became mandatory with tvOS 10.2.
|
||||||
PAIR_FRUIT,
|
PAIR_CLIENT_FRUIT,
|
||||||
// This is the Homekit type required for AirPlay 2 with both PIN setup and
|
// This is the Homekit type required for AirPlay 2 with both PIN setup and
|
||||||
// verification
|
// verification
|
||||||
PAIR_HOMEKIT_NORMAL,
|
PAIR_CLIENT_HOMEKIT_NORMAL,
|
||||||
// Same as normal except PIN is fixed to 3939 and stops after setup step 2,
|
// Same as normal except PIN is fixed to 3939 and stops after setup step 2,
|
||||||
// when session key is established
|
// when session key is established. This is the only mode where the server
|
||||||
PAIR_HOMEKIT_TRANSIENT,
|
// side is also supported.
|
||||||
|
PAIR_CLIENT_HOMEKIT_TRANSIENT,
|
||||||
|
PAIR_SERVER_HOMEKIT_TRANSIENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pair_setup_context;
|
struct pair_setup_context;
|
||||||
struct pair_verify_context;
|
struct pair_verify_context;
|
||||||
struct pair_cipher_context;
|
struct pair_cipher_context;
|
||||||
|
|
||||||
/* When you have the pin-code (must be 4 bytes), create a new context with this
|
/* Client
|
||||||
|
* When you have the pin-code (must be 4 bytes), create a new context with this
|
||||||
* function and then call pair_setup_request1(). device_id is only
|
* function and then call pair_setup_request1(). device_id is only
|
||||||
* required for homekit pairing, where it should have length 16.
|
* required for homekit pairing, where it should have length 16.
|
||||||
|
*
|
||||||
|
* Server
|
||||||
|
* Create a new context with the pin-code to verify with, then when the request
|
||||||
|
* is received use pair_setup_response1() to read it, and then reply with using
|
||||||
|
* pair_setup_request1().
|
||||||
*/
|
*/
|
||||||
struct pair_setup_context *
|
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, const char *device_id);
|
||||||
@ -34,6 +45,7 @@ pair_setup_free(struct pair_setup_context *sctx);
|
|||||||
const char *
|
const char *
|
||||||
pair_setup_errmsg(struct pair_setup_context *sctx);
|
pair_setup_errmsg(struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
|
||||||
uint8_t *
|
uint8_t *
|
||||||
pair_setup_request1(size_t *len, struct pair_setup_context *sctx);
|
pair_setup_request1(size_t *len, struct pair_setup_context *sctx);
|
||||||
uint8_t *
|
uint8_t *
|
||||||
@ -88,6 +100,7 @@ pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, siz
|
|||||||
int
|
int
|
||||||
pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, struct pair_verify_context *vctx);
|
pair_verify_result(const uint8_t **shared_secret, size_t *shared_secret_len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
|
|
||||||
/* When you have completed the verification you can extract a key with
|
/* 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
|
* pair_verify_result(). Give the shared secret as input to this function to
|
||||||
* create a ciphering context.
|
* create a ciphering context.
|
||||||
@ -123,4 +136,10 @@ pair_encrypt_rollback(struct pair_cipher_context *cctx);
|
|||||||
void
|
void
|
||||||
pair_decrypt_rollback(struct pair_cipher_context *cctx);
|
pair_decrypt_rollback(struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
/* For parsing an incoming message to see what type ("state") it is. Mostly
|
||||||
|
* useful for servers. Returns 1-6 for pair-setup and 1-4 for pair-verify.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len);
|
||||||
|
|
||||||
#endif /* !__PAIR_AP_H__ */
|
#endif /* !__PAIR_AP_H__ */
|
@ -594,33 +594,26 @@ encrypt_ctr(unsigned char *ciphertext, int ciphertext_len,
|
|||||||
|
|
||||||
/* -------------------------- IMPLEMENTATION -------------------------------- */
|
/* -------------------------- IMPLEMENTATION -------------------------------- */
|
||||||
|
|
||||||
static struct pair_setup_context *
|
static int
|
||||||
pair_setup_new(struct pair_definition *type, const char *pin, const char *device_id)
|
pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const char *device_id)
|
||||||
{
|
{
|
||||||
struct pair_setup_context *sctx;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
if (sodium_init() == -1)
|
||||||
return NULL;
|
return -1;
|
||||||
|
|
||||||
if (!pin || strlen(pin) < 4)
|
if (!pin || strlen(pin) < 4)
|
||||||
return NULL;
|
return -1;
|
||||||
|
|
||||||
sctx = calloc(1, sizeof(struct pair_setup_context));
|
|
||||||
if (!sctx)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
sctx->type = type;
|
|
||||||
|
|
||||||
memcpy(sctx->pin, pin, sizeof(sctx->pin));
|
memcpy(sctx->pin, pin, sizeof(sctx->pin));
|
||||||
|
|
||||||
return sctx;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pair_setup_free(struct pair_setup_context *sctx)
|
pair_client_setup_free(struct pair_setup_context *handle)
|
||||||
{
|
{
|
||||||
if (!sctx)
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
return;
|
|
||||||
|
|
||||||
srp_user_free(sctx->user);
|
srp_user_free(sctx->user);
|
||||||
|
|
||||||
@ -629,13 +622,12 @@ pair_setup_free(struct pair_setup_context *sctx)
|
|||||||
free(sctx->salt);
|
free(sctx->salt);
|
||||||
free(sctx->epk);
|
free(sctx->epk);
|
||||||
free(sctx->authtag);
|
free(sctx->authtag);
|
||||||
|
|
||||||
free(sctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
static uint8_t *
|
||||||
pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
|
pair_client_setup_request1(size_t *len, struct pair_setup_context *handle)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t method;
|
plist_t method;
|
||||||
plist_t user;
|
plist_t user;
|
||||||
@ -659,8 +651,9 @@ pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
static uint8_t *
|
||||||
pair_setup_request2(size_t *len, struct pair_setup_context *sctx)
|
pair_client_setup_request2(size_t *len, struct pair_setup_context *handle)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t pk;
|
plist_t pk;
|
||||||
plist_t proof;
|
plist_t proof;
|
||||||
@ -688,8 +681,9 @@ pair_setup_request2(size_t *len, struct pair_setup_context *sctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
static uint8_t *
|
||||||
pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
pair_client_setup_request3(size_t *len, struct pair_setup_context *handle)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t epk;
|
plist_t epk;
|
||||||
plist_t authtag;
|
plist_t authtag;
|
||||||
@ -707,21 +701,21 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
|||||||
session_key = srp_user_get_session_key(sctx->user, &session_key_len);
|
session_key = srp_user_get_session_key(sctx->user, &session_key_len);
|
||||||
if (!session_key)
|
if (!session_key)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup request 3: No valid session key";
|
handle->errmsg = "Setup request 3: No valid session key";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_SETUP_KEY, strlen(AES_SETUP_KEY), session_key, session_key_len);
|
ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_SETUP_KEY, strlen(AES_SETUP_KEY), session_key, session_key_len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup request 3: Hashing of key string and shared secret failed";
|
handle->errmsg = "Setup request 3: Hashing of key string and shared secret failed";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_SETUP_IV, strlen(AES_SETUP_IV), session_key, session_key_len);
|
ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_SETUP_IV, strlen(AES_SETUP_IV), session_key, session_key_len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup request 3: Hashing of iv string and shared secret failed";
|
handle->errmsg = "Setup request 3: Hashing of iv string and shared secret failed";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,7 +729,7 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
|||||||
ret = encrypt_gcm(encrypted, sizeof(encrypted), tag, sctx->public_key, sizeof(sctx->public_key), key, iv, &errmsg);
|
ret = encrypt_gcm(encrypted, sizeof(encrypted), tag, sctx->public_key, sizeof(sctx->public_key), key, iv, &errmsg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
sctx->errmsg = errmsg;
|
handle->errmsg = errmsg;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,8 +747,9 @@ pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t pk;
|
plist_t pk;
|
||||||
plist_t salt;
|
plist_t salt;
|
||||||
@ -765,7 +760,7 @@ pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
salt = plist_dict_get_item(dict, "salt");
|
salt = plist_dict_get_item(dict, "salt");
|
||||||
if (!pk || !salt)
|
if (!pk || !salt)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup response 1: Missing pk or salt";
|
handle->errmsg = "Setup response 1: Missing pk or salt";
|
||||||
plist_free(dict);
|
plist_free(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -779,8 +774,9 @@ pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t proof;
|
plist_t proof;
|
||||||
|
|
||||||
@ -789,7 +785,7 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
proof = plist_dict_get_item(dict, "proof");
|
proof = plist_dict_get_item(dict, "proof");
|
||||||
if (!proof)
|
if (!proof)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup response 2: Missing proof";
|
handle->errmsg = "Setup response 2: Missing proof";
|
||||||
plist_free(dict);
|
plist_free(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -802,7 +798,7 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
srp_user_verify_session(sctx->user, (const unsigned char *)sctx->M2);
|
srp_user_verify_session(sctx->user, (const unsigned char *)sctx->M2);
|
||||||
if (!srp_user_is_authenticated(sctx->user))
|
if (!srp_user_is_authenticated(sctx->user))
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup response 2: Server authentication failed";
|
handle->errmsg = "Setup response 2: Server authentication failed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,8 +806,9 @@ pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t epk;
|
plist_t epk;
|
||||||
plist_t authtag;
|
plist_t authtag;
|
||||||
@ -821,7 +818,7 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
epk = plist_dict_get_item(dict, "epk");
|
epk = plist_dict_get_item(dict, "epk");
|
||||||
if (!epk)
|
if (!epk)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup response 3: Missing epk";
|
handle->errmsg = "Setup response 3: Missing epk";
|
||||||
plist_free(dict);
|
plist_free(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -831,7 +828,7 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
authtag = plist_dict_get_item(dict, "authTag");
|
authtag = plist_dict_get_item(dict, "authTag");
|
||||||
if (!authtag)
|
if (!authtag)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup response 3: Missing authTag";
|
handle->errmsg = "Setup response 3: Missing authTag";
|
||||||
plist_free(dict);
|
plist_free(dict);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -840,17 +837,19 @@ pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_
|
|||||||
|
|
||||||
plist_free(dict);
|
plist_free(dict);
|
||||||
|
|
||||||
sctx->setup_is_completed = 1;
|
handle->setup_is_completed = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx)
|
pair_client_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *handle)
|
||||||
{
|
{
|
||||||
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
|
|
||||||
// Last 32 bytes of private key should match public key, but check assumption
|
// 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)
|
if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Pair setup result: Unexpected keys, private key does not match public key";
|
handle->errmsg = "Pair setup result: Unexpected keys, private key does not match public key";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,9 +859,55 @@ pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint8_t *
|
static int
|
||||||
pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, const char *device_id)
|
||||||
{
|
{
|
||||||
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
|
char hex[] = { 0, 0, 0 };
|
||||||
|
size_t hexkey_len;
|
||||||
|
const char *ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (sodium_init() == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!hexkey)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
hexkey_len = strlen(hexkey);
|
||||||
|
|
||||||
|
if (hexkey_len != 2 * sizeof(vctx->client_private_key))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (device_id && strlen(device_id) != 16)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (device_id)
|
||||||
|
memcpy(vctx->device_id, device_id, strlen(device_id));
|
||||||
|
|
||||||
|
ptr = hexkey;
|
||||||
|
for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2)
|
||||||
|
{
|
||||||
|
hex[0] = ptr[0];
|
||||||
|
hex[1] = ptr[1];
|
||||||
|
vctx->client_private_key[i] = strtol(hex, NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key);
|
||||||
|
for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2)
|
||||||
|
{
|
||||||
|
hex[0] = ptr[0];
|
||||||
|
hex[1] = ptr[1];
|
||||||
|
vctx->client_public_key[i] = strtol(hex, NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *
|
||||||
|
pair_client_verify_request1(size_t *len, struct pair_verify_context *handle)
|
||||||
|
{
|
||||||
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
const uint8_t basepoint[32] = {9};
|
const uint8_t basepoint[32] = {9};
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
int ret;
|
int ret;
|
||||||
@ -870,7 +915,7 @@ pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
|||||||
ret = crypto_scalarmult(vctx->client_eph_public_key, vctx->client_eph_private_key, basepoint);
|
ret = crypto_scalarmult(vctx->client_eph_public_key, vctx->client_eph_private_key, basepoint);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 1: Curve 25519 returned an error";
|
handle->errmsg = "Verify request 1: Curve 25519 returned an error";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +923,7 @@ pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
|||||||
data = calloc(1, *len);
|
data = calloc(1, *len);
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 1: Out of memory";
|
handle->errmsg = "Verify request 1: Out of memory";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -890,8 +935,9 @@ pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
static uint8_t *
|
||||||
pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
pair_client_verify_request2(size_t *len, struct pair_verify_context *handle)
|
||||||
{
|
{
|
||||||
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
uint8_t shared_secret[crypto_scalarmult_BYTES];
|
uint8_t shared_secret[crypto_scalarmult_BYTES];
|
||||||
uint8_t key[SHA512_DIGEST_LENGTH];
|
uint8_t key[SHA512_DIGEST_LENGTH];
|
||||||
uint8_t iv[SHA512_DIGEST_LENGTH];
|
uint8_t iv[SHA512_DIGEST_LENGTH];
|
||||||
@ -905,7 +951,7 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
|||||||
data = calloc(1, *len);
|
data = calloc(1, *len);
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 2: Out of memory";
|
handle->errmsg = "Verify request 2: Out of memory";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,28 +965,28 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
|||||||
ret = crypto_scalarmult(shared_secret, vctx->client_eph_private_key, vctx->server_eph_public_key);
|
ret = crypto_scalarmult(shared_secret, vctx->client_eph_private_key, vctx->server_eph_public_key);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 2: Curve 25519 returned an error";
|
handle->errmsg = "Verify request 2: Curve 25519 returned an error";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_VERIFY_KEY, strlen(AES_VERIFY_KEY), shared_secret, sizeof(shared_secret));
|
ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_VERIFY_KEY, strlen(AES_VERIFY_KEY), shared_secret, sizeof(shared_secret));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 2: Hashing of key string and shared secret failed";
|
handle->errmsg = "Verify request 2: Hashing of key string and shared secret failed";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_VERIFY_IV, strlen(AES_VERIFY_IV), shared_secret, sizeof(shared_secret));
|
ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_VERIFY_IV, strlen(AES_VERIFY_IV), shared_secret, sizeof(shared_secret));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 2: Hashing of iv string and shared secret failed";
|
handle->errmsg = "Verify request 2: Hashing of iv string and shared secret failed";
|
||||||
return NULL;
|
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_public_key, sizeof(vctx->server_public_key), signature, sizeof(signature), key, iv, &errmsg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
vctx->errmsg = errmsg;
|
handle->errmsg = errmsg;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,7 +994,7 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
|||||||
data = calloc(1, *len);
|
data = calloc(1, *len);
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify request 2: Out of memory";
|
handle->errmsg = "Verify request 2: Out of memory";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,14 +1004,15 @@ pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
pair_client_verify_response1(struct pair_verify_context *handle, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
size_t wanted;
|
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_public_key);
|
||||||
if (data_len < wanted)
|
if (data_len < wanted)
|
||||||
{
|
{
|
||||||
vctx->errmsg = "Verify response 2: Unexpected response (too short)";
|
handle->errmsg = "Verify response 2: Unexpected response (too short)";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,29 +1023,44 @@ pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
pair_client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len)
|
||||||
{
|
{
|
||||||
// TODO actually check response
|
// TODO actually check response
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pair_definition pair_fruit =
|
static int
|
||||||
|
pair_client_verify_result(const uint8_t **key, size_t *key_len, struct pair_verify_context *handle)
|
||||||
{
|
{
|
||||||
.pair_setup_new = pair_setup_new,
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
.pair_setup_free = pair_setup_free,
|
|
||||||
.pair_setup_result = pair_setup_result,
|
|
||||||
|
|
||||||
.pair_setup_request1 = pair_setup_request1,
|
*key = vctx->shared_secret;
|
||||||
.pair_setup_request2 = pair_setup_request2,
|
*key_len = sizeof(vctx->shared_secret);
|
||||||
.pair_setup_request3 = pair_setup_request3,
|
|
||||||
|
|
||||||
.pair_setup_response1 = pair_setup_response1,
|
return 0;
|
||||||
.pair_setup_response2 = pair_setup_response2,
|
}
|
||||||
.pair_setup_response3 = pair_setup_response3,
|
|
||||||
|
|
||||||
.pair_verify_request1 = pair_verify_request1,
|
|
||||||
.pair_verify_request2 = pair_verify_request2,
|
|
||||||
|
|
||||||
.pair_verify_response1 = pair_verify_response1,
|
struct pair_definition pair_client_fruit =
|
||||||
.pair_verify_response2 = pair_verify_response2,
|
{
|
||||||
|
.pair_setup_new = pair_client_setup_new,
|
||||||
|
.pair_setup_free = pair_client_setup_free,
|
||||||
|
.pair_setup_result = pair_client_setup_result,
|
||||||
|
|
||||||
|
.pair_setup_request1 = pair_client_setup_request1,
|
||||||
|
.pair_setup_request2 = pair_client_setup_request2,
|
||||||
|
.pair_setup_request3 = pair_client_setup_request3,
|
||||||
|
|
||||||
|
.pair_setup_response1 = pair_client_setup_response1,
|
||||||
|
.pair_setup_response2 = pair_client_setup_response2,
|
||||||
|
.pair_setup_response3 = pair_client_setup_response3,
|
||||||
|
|
||||||
|
.pair_verify_new = pair_client_verify_new,
|
||||||
|
.pair_verify_result = pair_client_verify_result,
|
||||||
|
|
||||||
|
.pair_verify_request1 = pair_client_verify_request1,
|
||||||
|
.pair_verify_request2 = pair_client_verify_request2,
|
||||||
|
|
||||||
|
.pair_verify_response1 = pair_client_verify_response1,
|
||||||
|
.pair_verify_response2 = pair_client_verify_response2,
|
||||||
};
|
};
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user