[airplay] Fix rare pairing bug due to incorrect SRP padding (ref #1280)

Update pair_ap to version 0.11, which it fixes an issue where some randomized
values of SRP "a" are short enough that they require padding.
This commit is contained in:
ejurgensen 2021-12-04 23:05:33 +01:00
parent 1418bfb245
commit 088e26c1f0
7 changed files with 1707 additions and 476 deletions

View File

@ -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);

View File

@ -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");

View File

@ -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

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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