mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-28 16:15:57 -05:00
Merge pull request #1363 from owntone/airplay_pairing1
Update pair_ap to version 0.11
This commit is contained in:
commit
aa56d0e849
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -1,6 +1,13 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sodium.h>
|
||||
|
||||
#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
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> // for isprint()
|
||||
#include <assert.h>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -4,7 +4,16 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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__ */
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <plist/plist.h>
|
||||
#include <sodium.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user