[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:
parent
1418bfb245
commit
088e26c1f0
|
@ -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);
|
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)
|
if (!rs->pair_setup_ctx)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Out of memory for verification setup context\n");
|
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);
|
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)
|
if (!rs->pair_verify_ctx)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Out of memory for verification verify context\n");
|
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)
|
response_handler_pair_setup2(struct evrtsp_request *req, struct airplay_session *rs)
|
||||||
{
|
{
|
||||||
enum airplay_seq_type seq_type;
|
enum airplay_seq_type seq_type;
|
||||||
const uint8_t *shared_secret;
|
struct pair_result *result;
|
||||||
size_t shared_secret_len;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
seq_type = response_handler_pair_generic(2, req, rs);
|
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)
|
if (rs->pair_type != PAIR_CLIENT_HOMEKIT_TRANSIENT)
|
||||||
return seq_type;
|
return seq_type;
|
||||||
|
|
||||||
ret = pair_setup_result(NULL, &shared_secret, &shared_secret_len, rs->pair_setup_ctx);
|
ret = pair_setup_result(NULL, &result, rs->pair_setup_ctx);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Transient setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx));
|
DPRINTF(E_LOG, L_AIRPLAY, "Transient setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx));
|
||||||
goto error;
|
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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Pair transient error setting up encryption for '%s'\n", rs->devname);
|
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)
|
if (seq_type != AIRPLAY_SEQ_CONTINUE)
|
||||||
return seq_type;
|
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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Pair setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx));
|
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;
|
struct output_device *device;
|
||||||
enum airplay_seq_type seq_type;
|
enum airplay_seq_type seq_type;
|
||||||
const uint8_t *shared_secret;
|
struct pair_result *result;
|
||||||
size_t shared_secret_len;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
seq_type = response_handler_pair_generic(5, req, rs);
|
seq_type = response_handler_pair_generic(5, req, rs);
|
||||||
if (seq_type != AIRPLAY_SEQ_CONTINUE)
|
if (seq_type != AIRPLAY_SEQ_CONTINUE)
|
||||||
goto error;
|
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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Pair verify result error: %s\n", pair_verify_errmsg(rs->pair_verify_ctx));
|
DPRINTF(E_LOG, L_AIRPLAY, "Pair verify result error: %s\n", pair_verify_errmsg(rs->pair_verify_ctx));
|
||||||
goto error;
|
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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_AIRPLAY, "Pair verify error setting up encryption for '%s'\n", rs->devname);
|
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)
|
if (!device)
|
||||||
goto error;
|
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);
|
ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -3872,7 +3872,7 @@ raop_cb_pair_setup_step3(struct evrtsp_request *req, void *arg)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Verification setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx));
|
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;
|
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)
|
if (!rs->pair_setup_ctx)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n");
|
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n");
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <sodium.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 SRPUser;
|
||||||
struct SRPVerifier;
|
struct SRPVerifier;
|
||||||
|
|
||||||
|
@ -8,8 +15,14 @@ struct pair_client_setup_context
|
||||||
{
|
{
|
||||||
struct SRPUser *user;
|
struct SRPUser *user;
|
||||||
|
|
||||||
char pin[4];
|
uint8_t pin[4];
|
||||||
char device_id[17]; // Incl. zero term
|
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;
|
const uint8_t *pkA;
|
||||||
int pkA_len;
|
int pkA_len;
|
||||||
|
@ -25,8 +38,6 @@ struct pair_client_setup_context
|
||||||
|
|
||||||
uint8_t *salt;
|
uint8_t *salt;
|
||||||
uint64_t salt_len;
|
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
|
// We don't actually use the server's epk and authtag for anything
|
||||||
uint8_t *epk;
|
uint8_t *epk;
|
||||||
|
@ -39,8 +50,16 @@ struct pair_server_setup_context
|
||||||
{
|
{
|
||||||
struct SRPVerifier *verifier;
|
struct SRPVerifier *verifier;
|
||||||
|
|
||||||
char pin[4];
|
uint8_t pin[4];
|
||||||
char device_id[17]; // Incl. zero term
|
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;
|
uint8_t *pkA;
|
||||||
uint64_t pkA_len;
|
uint64_t pkA_len;
|
||||||
|
@ -64,13 +83,24 @@ struct pair_server_setup_context
|
||||||
int salt_len;
|
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_setup_context
|
||||||
{
|
{
|
||||||
struct pair_definition *type;
|
struct pair_definition *type;
|
||||||
|
|
||||||
int setup_is_completed;
|
enum pair_status status;
|
||||||
const char *errmsg;
|
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
|
// Hex-formatet concatenation of public + private, 0-terminated
|
||||||
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
|
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
|
||||||
|
|
||||||
|
@ -83,30 +113,59 @@ struct pair_setup_context
|
||||||
|
|
||||||
struct pair_client_verify_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];
|
// These are the keys that were registered with the server in pair-setup
|
||||||
uint8_t server_public_key[64];
|
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];
|
bool verify_server_signature;
|
||||||
uint8_t client_private_key[crypto_sign_SECRETKEYBYTES];
|
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];
|
// For establishing the shared secret for encrypted communication
|
||||||
uint8_t client_eph_private_key[32];
|
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_verify_context
|
||||||
{
|
{
|
||||||
struct pair_definition *type;
|
struct pair_definition *type;
|
||||||
|
|
||||||
int verify_is_completed;
|
enum pair_status status;
|
||||||
const char *errmsg;
|
const char *errmsg;
|
||||||
|
|
||||||
|
struct pair_result result;
|
||||||
|
|
||||||
union pair_verify_union
|
union pair_verify_union
|
||||||
{
|
{
|
||||||
struct pair_client_verify_context client;
|
struct pair_client_verify_context client;
|
||||||
|
struct pair_server_verify_context server;
|
||||||
} vctx;
|
} vctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,38 +188,49 @@ struct pair_cipher_context
|
||||||
|
|
||||||
struct pair_definition
|
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);
|
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_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_request2)(size_t *len, struct pair_setup_context *sctx);
|
||||||
uint8_t *(*pair_setup_request3)(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_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);
|
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 *data, size_t data_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);
|
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_request1)(size_t *len, struct pair_verify_context *vctx);
|
||||||
uint8_t *(*pair_verify_request2)(size_t *len, struct pair_verify_context *vctx);
|
uint8_t *(*pair_verify_request2)(size_t *len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
int (*pair_verify_response1)(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 *data, size_t data_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);
|
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);
|
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_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, uint8_t *ciphertext, size_t ciphertext_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 --------------------- */
|
/* -------------------- GCRYPT AND OPENSSL COMPABILITY --------------------- */
|
||||||
/* partly borrowed from ffmpeg (rtmpdh.c) */
|
/* 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);
|
hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_len, const unsigned char *m2, int m2_len);
|
||||||
|
|
||||||
bnum
|
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
|
bnum
|
||||||
H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes);
|
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
|
#ifdef DEBUG_PAIR
|
||||||
void
|
void
|
||||||
hexdump(const char *msg, uint8_t *mem, size_t len);
|
hexdump(const char *msg, uint8_t *mem, size_t len);
|
||||||
|
|
||||||
|
void
|
||||||
|
bnum_dump(const char *msg, bnum n);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h> // for isprint()
|
#include <ctype.h> // for isprint()
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
|
@ -34,16 +35,37 @@
|
||||||
extern struct pair_definition pair_client_fruit;
|
extern struct pair_definition pair_client_fruit;
|
||||||
extern struct pair_definition pair_client_homekit_normal;
|
extern struct pair_definition pair_client_homekit_normal;
|
||||||
extern struct pair_definition pair_client_homekit_transient;
|
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
|
// Must be in sync with enum pair_type
|
||||||
static struct pair_definition *pair[] = {
|
static struct pair_definition *pair[] = {
|
||||||
&pair_client_fruit,
|
&pair_client_fruit,
|
||||||
&pair_client_homekit_normal,
|
&pair_client_homekit_normal,
|
||||||
&pair_client_homekit_transient,
|
&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 ------------------------ */
|
/* -------------------------- SHARED HASHING HELPERS ------------------------ */
|
||||||
|
|
||||||
int
|
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);
|
return hash_final(alg, &ctx, md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See rfc5054 PAD()
|
||||||
bnum
|
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;
|
bnum bn;
|
||||||
unsigned char *bin;
|
unsigned char *bin;
|
||||||
unsigned char buff[SHA512_DIGEST_LENGTH];
|
unsigned char buff[SHA512_DIGEST_LENGTH];
|
||||||
int len_n1 = bnum_num_bytes(n1);
|
int len_n1 = bnum_num_bytes(n1);
|
||||||
int len_n2 = bnum_num_bytes(n2);
|
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))
|
assert(len_n1 <= padded_len);
|
||||||
return 0;
|
assert(len_n2 <= padded_len);
|
||||||
|
|
||||||
bin = calloc( 1, nbytes );
|
bin = calloc(1, nbytes);
|
||||||
|
|
||||||
bnum_bn2bin(n1, bin, len_n1);
|
bnum_bn2bin(n1, bin + offset_n1, len_n1);
|
||||||
bnum_bn2bin(n2, bin + nbytes - len_n2, len_n2);
|
bnum_bn2bin(n2, bin + offset_n2, len_n2);
|
||||||
hash( alg, bin, nbytes, buff );
|
hash(alg, bin, nbytes, buff);
|
||||||
free(bin);
|
free(bin);
|
||||||
bnum_bin2bn(bn, buff, hash_length(alg));
|
bnum_bin2bn(bn, buff, hash_length(alg));
|
||||||
return bn;
|
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);
|
unsigned char *bin = malloc(nbytes);
|
||||||
|
|
||||||
bnum_bn2bin(n, bin, len_n);
|
bnum_bn2bin(n, bin, len_n);
|
||||||
memcpy( bin + len_n, bytes, len_bytes );
|
memcpy(bin + len_n, bytes, len_bytes);
|
||||||
hash( alg, bin, nbytes, buff );
|
hash(alg, bin, nbytes, buff);
|
||||||
free(bin);
|
free(bin);
|
||||||
bnum_bin2bn(bn, buff, hash_length(alg));
|
bnum_bin2bn(bn, buff, hash_length(alg));
|
||||||
return bn;
|
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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------- API -----------------------------------*/
|
/* ----------------------------------- API -----------------------------------*/
|
||||||
|
|
||||||
struct pair_setup_context *
|
struct pair_setup_context *
|
||||||
pair_setup_new(enum pair_type type, const char *pin, const char *device_id)
|
pair_setup_new(enum pair_type type, const char *pin, pair_cb add_cb, void *cb_arg, const char *device_id)
|
||||||
{
|
{
|
||||||
struct pair_setup_context *sctx;
|
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];
|
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);
|
free(sctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -314,6 +349,76 @@ pair_setup_errmsg(struct pair_setup_context *sctx)
|
||||||
return sctx->errmsg;
|
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 *
|
uint8_t *
|
||||||
pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
|
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
|
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)
|
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 -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sctx->type->pair_setup_response1(sctx, data, data_len);
|
return sctx->type->pair_setup_response1(sctx, in, in_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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)
|
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 -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sctx->type->pair_setup_response2(sctx, data, data_len);
|
return sctx->type->pair_setup_response2(sctx, in, in_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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)
|
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;
|
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 -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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;
|
if (sctx->status != PAIR_STATUS_COMPLETED)
|
||||||
size_t out_len;
|
|
||||||
char *ptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!sctx->setup_is_completed)
|
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup result: The pair setup has not been completed";
|
sctx->errmsg = "Setup result: Pair setup has not been completed";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sctx->type->pair_setup_result)
|
if (sctx->type->pair_setup_result)
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup result: Unsupported";
|
if (sctx->type->pair_setup_result(sctx) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sctx->type->pair_setup_result(&out_key, &out_len, sctx) != 0)
|
if (client_setup_keys)
|
||||||
{
|
*client_setup_keys = sctx->result_str;
|
||||||
return -1;
|
if (result)
|
||||||
}
|
*result = &sctx->result;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct pair_verify_context *
|
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;
|
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];
|
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);
|
free(vctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -477,6 +557,64 @@ pair_verify_errmsg(struct pair_verify_context *vctx)
|
||||||
return vctx->errmsg;
|
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 *
|
uint8_t *
|
||||||
pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
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
|
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)
|
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 -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vctx->type->pair_verify_response1(vctx, data, data_len);
|
return vctx->type->pair_verify_response1(vctx, in, in_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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)
|
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;
|
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;
|
return -1;
|
||||||
|
|
||||||
vctx->verify_is_completed = 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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";
|
vctx->errmsg = "Verify result: The pairing verification did not complete";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vctx->type->pair_verify_result)
|
if (vctx->type->pair_verify_result)
|
||||||
return -1;
|
{
|
||||||
|
if (vctx->type->pair_verify_result(vctx) != 0)
|
||||||
if (vctx->type->pair_verify_result(shared_secret, shared_secret_len, vctx) != 0)
|
return -1;
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
*result = &vctx->result;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +714,7 @@ pair_cipher_errmsg(struct pair_cipher_context *cctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
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)
|
if (!cctx->type->pair_encrypt)
|
||||||
{
|
{
|
||||||
|
@ -587,7 +726,7 @@ pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, s
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t
|
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)
|
if (!cctx->type->pair_decrypt)
|
||||||
{
|
{
|
||||||
|
@ -611,7 +750,40 @@ pair_decrypt_rollback(struct pair_cipher_context *cctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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)
|
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 -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>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define PAIR_AP_VERSION_MAJOR 0
|
#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
|
enum pair_type
|
||||||
{
|
{
|
||||||
|
@ -15,28 +24,80 @@ enum pair_type
|
||||||
// verification
|
// verification
|
||||||
PAIR_CLIENT_HOMEKIT_NORMAL,
|
PAIR_CLIENT_HOMEKIT_NORMAL,
|
||||||
// Same as normal except PIN is fixed to 3939 and stops after setup step 2,
|
// Same as normal except PIN is fixed to 3939 and stops after setup step 2,
|
||||||
// when session key is established. This is the only mode where the server
|
// when session key is established
|
||||||
// side is also supported.
|
|
||||||
PAIR_CLIENT_HOMEKIT_TRANSIENT,
|
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_setup_context;
|
||||||
struct pair_verify_context;
|
struct pair_verify_context;
|
||||||
struct pair_cipher_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
|
/* Client
|
||||||
* When you have the pin-code (must be 4 bytes), create a new context with this
|
* When you have the pin-code (must be 4 chars), create a new context with this
|
||||||
* function and then call pair_setup_request1(). device_id is only
|
* function and then call pair_setup() or pair_setup_request1(). device_id is
|
||||||
* required for homekit pairing, where it should have length 16.
|
* 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
|
* Server
|
||||||
* Create a new context with the pin-code to verify with, then when the request
|
* The client will make a connection and then at some point make a /pair-setup
|
||||||
* is received use pair_setup_response1() to read it, and then reply with using
|
* or a /pair-verify. The server should:
|
||||||
* pair_setup_request1().
|
* - 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 *
|
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
|
void
|
||||||
pair_setup_free(struct pair_setup_context *sctx);
|
pair_setup_free(struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
@ -45,7 +106,25 @@ pair_setup_free(struct pair_setup_context *sctx);
|
||||||
const char *
|
const char *
|
||||||
pair_setup_errmsg(struct pair_setup_context *sctx);
|
pair_setup_errmsg(struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
/* 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 *
|
uint8_t *
|
||||||
pair_setup_request1(size_t *len, struct pair_setup_context *sctx);
|
pair_setup_request1(size_t *len, struct pair_setup_context *sctx);
|
||||||
uint8_t *
|
uint8_t *
|
||||||
|
@ -54,28 +133,34 @@ uint8_t *
|
||||||
pair_setup_request3(size_t *len, struct pair_setup_context *sctx);
|
pair_setup_request3(size_t *len, struct pair_setup_context *sctx);
|
||||||
|
|
||||||
int
|
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
|
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
|
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);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
|
|
||||||
/* When you have completed the setup you can extract a key with
|
/* ------------------------------ pair verify ------------------------------- */
|
||||||
* pair_setup_result(). Give the string as input to this function to
|
|
||||||
* create a verification context and then call pair_verify_request1()
|
/* Client
|
||||||
* device_id is only required for homekit pairing, where it should have len 16.
|
* 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 *
|
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
|
void
|
||||||
pair_verify_free(struct pair_verify_context *vctx);
|
pair_verify_free(struct pair_verify_context *vctx);
|
||||||
|
|
||||||
|
@ -84,25 +169,38 @@ pair_verify_free(struct pair_verify_context *vctx);
|
||||||
const char *
|
const char *
|
||||||
pair_verify_errmsg(struct pair_verify_context *vctx);
|
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 *
|
uint8_t *
|
||||||
pair_verify_request1(size_t *len, struct pair_verify_context *vctx);
|
pair_verify_request1(size_t *len, struct pair_verify_context *vctx);
|
||||||
uint8_t *
|
uint8_t *
|
||||||
pair_verify_request2(size_t *len, struct pair_verify_context *vctx);
|
pair_verify_request2(size_t *len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
int
|
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
|
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);
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
|
|
||||||
/* When you have completed the verification you can extract a key with
|
/* ------------------------------- ciphering -------------------------------- */
|
||||||
* pair_verify_result(). Give the shared secret as input to this function to
|
|
||||||
|
/* 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.
|
* create a ciphering context.
|
||||||
*/
|
*/
|
||||||
struct pair_cipher_context *
|
struct pair_cipher_context *
|
||||||
|
@ -120,14 +218,14 @@ pair_cipher_errmsg(struct pair_cipher_context *cctx);
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
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
|
/* 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
|
* return value == ciphertext_len then everything was decrypted. On error -1 is
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
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
|
/* Rolls back the nonce
|
||||||
*/
|
*/
|
||||||
|
@ -136,10 +234,33 @@ pair_encrypt_rollback(struct pair_cipher_context *cctx);
|
||||||
void
|
void
|
||||||
pair_decrypt_rollback(struct pair_cipher_context *cctx);
|
pair_decrypt_rollback(struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------- 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
|
/* 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.
|
* useful for servers. Returns 1-6 for pair-setup and 1-4 for pair-verify.
|
||||||
*/
|
*/
|
||||||
int
|
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__ */
|
#endif /* !__PAIR_AP_H__ */
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
#include <plist/plist.h>
|
#include <plist/plist.h>
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "pair-internal.h"
|
#include "pair-internal.h"
|
||||||
|
|
||||||
/* ----------------------------- DEFINES ETC ------------------------------- */
|
/* ----------------------------- DEFINES ETC ------------------------------- */
|
||||||
|
@ -58,6 +60,7 @@ typedef struct
|
||||||
{
|
{
|
||||||
bnum N;
|
bnum N;
|
||||||
bnum g;
|
bnum g;
|
||||||
|
int N_len;
|
||||||
} NGConstant;
|
} NGConstant;
|
||||||
|
|
||||||
struct SRPUser
|
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->N, n_hex);
|
||||||
bnum_hex2bn(ng->g, g_hex);
|
bnum_hex2bn(ng->g, g_hex);
|
||||||
|
|
||||||
|
ng->N_len = bnum_num_bytes(ng->N);
|
||||||
|
|
||||||
return ng;
|
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(s, bytes_s, len_s);
|
||||||
bnum_bin2bn(B, bytes_B, len_B);
|
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(v);
|
||||||
bnum_new(tmp1);
|
bnum_new(tmp1);
|
||||||
bnum_new(tmp2);
|
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)
|
if (!s || !B || !k || !v || !tmp1 || !tmp2 || !tmp3)
|
||||||
goto cleanup1;
|
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);
|
x = calculate_x(usr->alg, s, usr->username, usr->password, usr->password_len);
|
||||||
if (!u || !x)
|
if (!u || !x)
|
||||||
goto cleanup2;
|
goto cleanup2;
|
||||||
|
@ -595,11 +600,11 @@ encrypt_ctr(unsigned char *ciphertext, int ciphertext_len,
|
||||||
/* -------------------------- IMPLEMENTATION -------------------------------- */
|
/* -------------------------- IMPLEMENTATION -------------------------------- */
|
||||||
|
|
||||||
static int
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
if (!is_initialized())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!pin || strlen(pin) < 4)
|
if (!pin || strlen(pin) < 4)
|
||||||
|
@ -611,7 +616,7 @@ pair_client_setup_new(struct pair_setup_context *handle, const char *pin, const
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
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 *
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
|
@ -651,7 +656,7 @@ pair_client_setup_request1(size_t *len, struct pair_setup_context *handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
|
@ -681,7 +686,7 @@ pair_client_setup_request2(size_t *len, struct pair_setup_context *handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
|
@ -747,7 +752,7 @@ pair_client_setup_request3(size_t *len, struct pair_setup_context *handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
|
@ -774,7 +779,7 @@ pair_client_setup_response1(struct pair_setup_context *handle, const uint8_t *da
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
|
@ -806,7 +811,7 @@ pair_client_setup_response2(struct pair_setup_context *handle, const uint8_t *da
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct pair_client_setup_context *sctx = &handle->sctx.client;
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
|
@ -837,30 +842,36 @@ pair_client_setup_response3(struct pair_setup_context *handle, const uint8_t *da
|
||||||
|
|
||||||
plist_free(dict);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
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
|
// Last 32 bytes of the private key is the public key, so we don't need to
|
||||||
if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0)
|
// explicitly export that
|
||||||
{
|
ptr = handle->result_str;
|
||||||
handle->errmsg = "Pair setup result: Unexpected keys, private key does not match public key";
|
for (i = 0; i < sizeof(sctx->private_key); i++)
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
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;
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
char hex[] = { 0, 0, 0 };
|
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;
|
const char *ptr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
if (!is_initialized())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!hexkey)
|
if (!client_setup_keys)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
hexkey_len = strlen(hexkey);
|
hexkey_len = strlen(client_setup_keys);
|
||||||
|
|
||||||
if (hexkey_len != 2 * sizeof(vctx->client_private_key))
|
if (hexkey_len != 2 * sizeof(vctx->client_private_key))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -885,7 +896,7 @@ pair_client_verify_new(struct pair_verify_context *handle, const char *hexkey, c
|
||||||
if (device_id)
|
if (device_id)
|
||||||
memcpy(vctx->device_id, device_id, strlen(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)
|
for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2)
|
||||||
{
|
{
|
||||||
hex[0] = ptr[0];
|
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);
|
vctx->client_private_key[i] = strtol(hex, NULL, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key);
|
crypto_sign_ed25519_sk_to_pk(vctx->client_public_key, vctx->client_private_key);
|
||||||
for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2)
|
|
||||||
{
|
|
||||||
hex[0] = ptr[0];
|
|
||||||
hex[1] = ptr[1];
|
|
||||||
vctx->client_public_key[i] = strtol(hex, NULL, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *
|
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;
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
const uint8_t basepoint[32] = {9};
|
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 *
|
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;
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
uint8_t shared_secret[crypto_scalarmult_BYTES];
|
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;
|
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)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
handle->errmsg = errmsg;
|
handle->errmsg = errmsg;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*len = 4 + sizeof(vctx->server_public_key);
|
*len = 4 + sizeof(vctx->server_fruit_public_key);
|
||||||
data = calloc(1, *len);
|
data = calloc(1, *len);
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
|
@ -998,18 +1003,18 @@ pair_client_verify_request2(size_t *len, struct pair_verify_context *handle)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data + 4, encrypted, sizeof(vctx->server_public_key));
|
memcpy(data + 4, encrypted, sizeof(vctx->server_fruit_public_key));
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
size_t wanted;
|
size_t wanted;
|
||||||
|
|
||||||
wanted = sizeof(vctx->server_eph_public_key) + sizeof(vctx->server_public_key);
|
wanted = sizeof(vctx->server_eph_public_key) + sizeof(vctx->server_fruit_public_key);
|
||||||
if (data_len < wanted)
|
if (data_len < wanted)
|
||||||
{
|
{
|
||||||
handle->errmsg = "Verify response 2: Unexpected response (too short)";
|
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_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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pair_client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len)
|
client_verify_response2(struct pair_verify_context *handle, const uint8_t *data, size_t data_len)
|
||||||
{
|
|
||||||
// TODO actually check response
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
pair_client_verify_result(const uint8_t **key, size_t *key_len, struct pair_verify_context *handle)
|
|
||||||
{
|
{
|
||||||
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
struct pair_client_verify_context *vctx = &handle->vctx.client;
|
||||||
|
// TODO actually check response
|
||||||
|
|
||||||
*key = vctx->shared_secret;
|
memcpy(handle->result.shared_secret, vctx->shared_secret, sizeof(vctx->shared_secret));
|
||||||
*key_len = sizeof(vctx->shared_secret);
|
handle->result.shared_secret_len = sizeof(vctx->shared_secret);
|
||||||
|
|
||||||
|
handle->status = PAIR_STATUS_COMPLETED;
|
||||||
|
|
||||||
return 0;
|
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 =
|
struct pair_definition pair_client_fruit =
|
||||||
{
|
{
|
||||||
.pair_setup_new = pair_client_setup_new,
|
.pair_setup_new = client_setup_new,
|
||||||
.pair_setup_free = pair_client_setup_free,
|
.pair_setup_free = client_setup_free,
|
||||||
.pair_setup_result = pair_client_setup_result,
|
.pair_setup_result = client_setup_result,
|
||||||
|
|
||||||
.pair_setup_request1 = pair_client_setup_request1,
|
.pair_setup_request1 = client_setup_request1,
|
||||||
.pair_setup_request2 = pair_client_setup_request2,
|
.pair_setup_request2 = client_setup_request2,
|
||||||
.pair_setup_request3 = pair_client_setup_request3,
|
.pair_setup_request3 = client_setup_request3,
|
||||||
|
|
||||||
.pair_setup_response1 = pair_client_setup_response1,
|
.pair_setup_response1 = client_setup_response1,
|
||||||
.pair_setup_response2 = pair_client_setup_response2,
|
.pair_setup_response2 = client_setup_response2,
|
||||||
.pair_setup_response3 = pair_client_setup_response3,
|
.pair_setup_response3 = client_setup_response3,
|
||||||
|
|
||||||
.pair_verify_new = pair_client_verify_new,
|
.pair_verify_new = client_verify_new,
|
||||||
.pair_verify_result = pair_client_verify_result,
|
|
||||||
|
|
||||||
.pair_verify_request1 = pair_client_verify_request1,
|
.pair_verify_request1 = client_verify_request1,
|
||||||
.pair_verify_request2 = pair_client_verify_request2,
|
.pair_verify_request2 = client_verify_request2,
|
||||||
|
|
||||||
.pair_verify_response1 = pair_client_verify_response1,
|
.pair_verify_response1 = client_verify_response1,
|
||||||
.pair_verify_response2 = pair_client_verify_response2,
|
.pair_verify_response2 = client_verify_response2,
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue