[airplay] Add support for AirPlay 2
Includes - Implementation in src/outputs/airplays2, type OUTPUT_TYPE_AIRPLAY - Homekit pairing, both normal (with PIN) and transient - New session startup sequence, incl GET /info, SETPEERS and 2 x SETUP - No more OPTIONS and ANNOUNCE - Use POST /feedback for keepalive instead of SET_PARAMETERS - Sequence dispatching instead of callback chains - Continue despite "Bad request" to SET_PARAMETER (volume) - Opening of event connection to receiver (reverse rtsp connection) Still to be done - Password authentication - Handling of events
This commit is contained in:
parent
86f762bb1f
commit
8368ca7686
|
@ -26,7 +26,8 @@ MPD_SRC=mpd.c mpd.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if COND_RAOP_VERIFICATION
|
if COND_RAOP_VERIFICATION
|
||||||
RAOP_VERIFICATION_SRC=outputs/raop_verification.c outputs/raop_verification.h
|
RAOP_VERIFICATION_SRC=outputs/pair_fruit.c outputs/pair_homekit.c outputs/pair.c outputs/pair-internal.h outputs/pair.h
|
||||||
|
AM_CPPFLAGS += -DCONFIG_GCRYPT
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if COND_ALSA
|
if COND_ALSA
|
||||||
|
@ -134,6 +135,7 @@ forked_daapd_SOURCES = main.c \
|
||||||
outputs.h outputs.c \
|
outputs.h outputs.c \
|
||||||
outputs/rtp_common.h outputs/rtp_common.c \
|
outputs/rtp_common.h outputs/rtp_common.c \
|
||||||
outputs/raop.c $(RAOP_VERIFICATION_SRC) \
|
outputs/raop.c $(RAOP_VERIFICATION_SRC) \
|
||||||
|
outputs/airplay.c \
|
||||||
outputs/streaming.c outputs/dummy.c outputs/fifo.c \
|
outputs/streaming.c outputs/dummy.c outputs/fifo.c \
|
||||||
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
|
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
|
||||||
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
|
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
|
||||||
|
|
|
@ -45,8 +45,9 @@ extern "C" {
|
||||||
|
|
||||||
/* Response codes */
|
/* Response codes */
|
||||||
#define RTSP_OK 200
|
#define RTSP_OK 200
|
||||||
#define RTSP_UNAUTHORIZED 401
|
#define RTSP_UNAUTHORIZED 401
|
||||||
#define RTSP_FORBIDDEN 403
|
#define RTSP_FORBIDDEN 403
|
||||||
|
#define RTSP_CONNECTION_AUTH_REQUIRED 470
|
||||||
|
|
||||||
struct evrtsp_connection;
|
struct evrtsp_connection;
|
||||||
|
|
||||||
|
@ -135,6 +136,10 @@ void evrtsp_connection_free(struct evrtsp_connection *evcon);
|
||||||
void evrtsp_connection_set_closecb(struct evrtsp_connection *evcon,
|
void evrtsp_connection_set_closecb(struct evrtsp_connection *evcon,
|
||||||
void (*)(struct evrtsp_connection *, void *), void *);
|
void (*)(struct evrtsp_connection *, void *), void *);
|
||||||
|
|
||||||
|
/** Set a callback for encryption/decryption. */
|
||||||
|
void evrtsp_connection_set_ciphercb(struct evrtsp_connection *evcon,
|
||||||
|
void (*)(struct evbuffer *, void *, int encrypt), void *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associates an event base with the connection - can only be called
|
* Associates an event base with the connection - can only be called
|
||||||
* on a freshly created connection object that has not been used yet.
|
* on a freshly created connection object that has not been used yet.
|
||||||
|
|
|
@ -85,6 +85,9 @@ struct evrtsp_connection {
|
||||||
void (*closecb)(struct evrtsp_connection *, void *);
|
void (*closecb)(struct evrtsp_connection *, void *);
|
||||||
void *closecb_arg;
|
void *closecb_arg;
|
||||||
|
|
||||||
|
void (*ciphercb)(struct evbuffer *evbuf, void *, int encrypt);
|
||||||
|
void *ciphercb_arg;
|
||||||
|
|
||||||
struct event_base *base;
|
struct event_base *base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -620,6 +620,9 @@ evrtsp_read(int fd, short what, void *arg)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evcon->ciphercb)
|
||||||
|
evcon->ciphercb(evcon->input_buffer, evcon->ciphercb_arg, 0);
|
||||||
|
|
||||||
switch (evcon->state) {
|
switch (evcon->state) {
|
||||||
case EVCON_READING_FIRSTLINE:
|
case EVCON_READING_FIRSTLINE:
|
||||||
evrtsp_read_firstline(evcon, req);
|
evrtsp_read_firstline(evcon, req);
|
||||||
|
@ -725,6 +728,10 @@ evrtsp_request_dispatch(struct evrtsp_connection* evcon)
|
||||||
/* Create the header from the store arguments */
|
/* Create the header from the store arguments */
|
||||||
evrtsp_make_header(evcon, req);
|
evrtsp_make_header(evcon, req);
|
||||||
|
|
||||||
|
/* forked-daapd customisation for encryption */
|
||||||
|
if (evcon->ciphercb)
|
||||||
|
evcon->ciphercb(evcon->output_buffer, evcon->ciphercb_arg, 1);
|
||||||
|
|
||||||
evrtsp_write_buffer(evcon, evrtsp_write_connectioncb, NULL);
|
evrtsp_write_buffer(evcon, evrtsp_write_connectioncb, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1312,6 +1319,14 @@ evrtsp_connection_set_closecb(struct evrtsp_connection *evcon,
|
||||||
evcon->closecb_arg = cbarg;
|
evcon->closecb_arg = cbarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
evrtsp_connection_set_ciphercb(struct evrtsp_connection *evcon,
|
||||||
|
void (*cb)(struct evbuffer *, void *, int encrypt), void *cbarg)
|
||||||
|
{
|
||||||
|
evcon->ciphercb = cb;
|
||||||
|
evcon->ciphercb_arg = cbarg;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evrtsp_connection_get_local_address(struct evrtsp_connection *evcon,
|
evrtsp_connection_get_local_address(struct evrtsp_connection *evcon,
|
||||||
char **address, u_short *port, int *family)
|
char **address, u_short *port, int *family)
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
|
|
||||||
extern struct output_definition output_raop;
|
extern struct output_definition output_raop;
|
||||||
|
extern struct output_definition output_airplay;
|
||||||
extern struct output_definition output_streaming;
|
extern struct output_definition output_streaming;
|
||||||
extern struct output_definition output_dummy;
|
extern struct output_definition output_dummy;
|
||||||
extern struct output_definition output_fifo;
|
extern struct output_definition output_fifo;
|
||||||
|
@ -59,6 +60,7 @@ extern struct event_base *evbase_player;
|
||||||
// Must be in sync with enum output_types
|
// Must be in sync with enum output_types
|
||||||
static struct output_definition *outputs[] = {
|
static struct output_definition *outputs[] = {
|
||||||
&output_raop,
|
&output_raop,
|
||||||
|
&output_airplay,
|
||||||
&output_streaming,
|
&output_streaming,
|
||||||
&output_dummy,
|
&output_dummy,
|
||||||
&output_fifo,
|
&output_fifo,
|
||||||
|
@ -1226,6 +1228,11 @@ outputs_init(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outputs[i]->disabled)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!outputs[i]->init)
|
if (!outputs[i]->init)
|
||||||
{
|
{
|
||||||
no_output = 0;
|
no_output = 0;
|
||||||
|
|
|
@ -59,6 +59,7 @@ typedef int (*output_metadata_finalize_cb)(struct output_metadata *metadata);
|
||||||
enum output_types
|
enum output_types
|
||||||
{
|
{
|
||||||
OUTPUT_TYPE_RAOP,
|
OUTPUT_TYPE_RAOP,
|
||||||
|
OUTPUT_TYPE_AIRPLAY,
|
||||||
OUTPUT_TYPE_STREAMING,
|
OUTPUT_TYPE_STREAMING,
|
||||||
OUTPUT_TYPE_DUMMY,
|
OUTPUT_TYPE_DUMMY,
|
||||||
OUTPUT_TYPE_FIFO,
|
OUTPUT_TYPE_FIFO,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,240 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
struct SRPUser;
|
||||||
|
|
||||||
|
struct pair_setup_context
|
||||||
|
{
|
||||||
|
struct pair_definition *type;
|
||||||
|
|
||||||
|
struct SRPUser *user;
|
||||||
|
|
||||||
|
char pin[4];
|
||||||
|
char device_id[17]; // Incl. zero term
|
||||||
|
|
||||||
|
const uint8_t *pkA;
|
||||||
|
int pkA_len;
|
||||||
|
|
||||||
|
uint8_t *pkB;
|
||||||
|
uint64_t pkB_len;
|
||||||
|
|
||||||
|
const uint8_t *M1;
|
||||||
|
int M1_len;
|
||||||
|
|
||||||
|
uint8_t *M2;
|
||||||
|
uint64_t M2_len;
|
||||||
|
|
||||||
|
uint8_t *salt;
|
||||||
|
uint64_t salt_len;
|
||||||
|
uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
|
||||||
|
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
|
// Hex-formatet concatenation of public + private, 0-terminated
|
||||||
|
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
|
||||||
|
|
||||||
|
// We don't actually use the server's epk and authtag for anything
|
||||||
|
uint8_t *epk;
|
||||||
|
uint64_t epk_len;
|
||||||
|
uint8_t *authtag;
|
||||||
|
uint64_t authtag_len;
|
||||||
|
|
||||||
|
int setup_is_completed;
|
||||||
|
const char *errmsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_verify_context
|
||||||
|
{
|
||||||
|
struct pair_definition *type;
|
||||||
|
|
||||||
|
char device_id[17]; // Incl. zero term
|
||||||
|
|
||||||
|
uint8_t server_eph_public_key[32];
|
||||||
|
uint8_t server_public_key[64];
|
||||||
|
|
||||||
|
uint8_t client_public_key[crypto_sign_PUBLICKEYBYTES];
|
||||||
|
uint8_t client_private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
|
|
||||||
|
uint8_t client_eph_public_key[32];
|
||||||
|
uint8_t client_eph_private_key[32];
|
||||||
|
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
|
||||||
|
int verify_is_completed;
|
||||||
|
const char *errmsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_cipher_context
|
||||||
|
{
|
||||||
|
struct pair_definition *type;
|
||||||
|
|
||||||
|
uint8_t encryption_key[32];
|
||||||
|
uint8_t decryption_key[32];
|
||||||
|
|
||||||
|
uint64_t encryption_counter;
|
||||||
|
uint64_t decryption_counter;
|
||||||
|
|
||||||
|
const char *errmsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_definition
|
||||||
|
{
|
||||||
|
struct pair_setup_context *(*pair_setup_new)(struct pair_definition *type, const char *pin, 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);
|
||||||
|
|
||||||
|
uint8_t *(*pair_setup_request1)(uint32_t *len, struct pair_setup_context *sctx);
|
||||||
|
uint8_t *(*pair_setup_request2)(uint32_t *len, struct pair_setup_context *sctx);
|
||||||
|
uint8_t *(*pair_setup_request3)(uint32_t *len, struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
int (*pair_setup_response1)(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int (*pair_setup_response2)(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int (*pair_setup_response3)(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
|
||||||
|
uint8_t *(*pair_verify_request1)(uint32_t *len, struct pair_verify_context *vctx);
|
||||||
|
uint8_t *(*pair_verify_request2)(uint32_t *len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
|
int (*pair_verify_response1)(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int (*pair_verify_response2)(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_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);
|
||||||
|
|
||||||
|
int (*pair_encrypt)(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
|
||||||
|
int (*pair_decrypt)(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------- GCRYPT AND OPENSSL COMPABILITY --------------------- */
|
||||||
|
/* partly borrowed from ffmpeg (rtmpdh.c) */
|
||||||
|
|
||||||
|
#if CONFIG_GCRYPT
|
||||||
|
#include <gcrypt.h>
|
||||||
|
#define SHA512_DIGEST_LENGTH 64
|
||||||
|
#define bnum_new(bn) \
|
||||||
|
do { \
|
||||||
|
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { \
|
||||||
|
if (!gcry_check_version("1.5.4")) \
|
||||||
|
abort(); \
|
||||||
|
gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \
|
||||||
|
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \
|
||||||
|
} \
|
||||||
|
bn = gcry_mpi_new(1); \
|
||||||
|
} while (0)
|
||||||
|
#define bnum_free(bn) gcry_mpi_release(bn)
|
||||||
|
#define bnum_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
|
||||||
|
#define bnum_is_zero(bn) (gcry_mpi_cmp_ui(bn, (unsigned long)0) == 0)
|
||||||
|
#define bnum_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
|
||||||
|
#define bnum_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
|
||||||
|
#define bnum_hex2bn(bn, buf) gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0)
|
||||||
|
#define bnum_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
|
||||||
|
#define bnum_add(bn, a, b) gcry_mpi_add(bn, a, b)
|
||||||
|
#define bnum_sub(bn, a, b) gcry_mpi_sub(bn, a, b)
|
||||||
|
#define bnum_mul(bn, a, b) gcry_mpi_mul(bn, a, b)
|
||||||
|
typedef gcry_mpi_t bnum;
|
||||||
|
__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
||||||
|
{
|
||||||
|
gcry_mpi_powm(bn, y, q, p);
|
||||||
|
}
|
||||||
|
#elif CONFIG_OPENSSL
|
||||||
|
#include <openssl/crypto.h>
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#define bnum_new(bn) bn = BN_new()
|
||||||
|
#define bnum_free(bn) BN_free(bn)
|
||||||
|
#define bnum_num_bytes(bn) BN_num_bytes(bn)
|
||||||
|
#define bnum_is_zero(bn) BN_is_zero(bn)
|
||||||
|
#define bnum_bn2bin(bn, buf, len) BN_bn2bin(bn, buf)
|
||||||
|
#define bnum_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0)
|
||||||
|
#define bnum_hex2bn(bn, buf) BN_hex2bn(&bn, buf)
|
||||||
|
#define bnum_random(bn, num_bits) BN_rand(bn, num_bits, 0, 0)
|
||||||
|
#define bnum_add(bn, a, b) BN_add(bn, a, b)
|
||||||
|
#define bnum_sub(bn, a, b) BN_sub(bn, a, b)
|
||||||
|
typedef BIGNUM* bnum;
|
||||||
|
__attribute__((unused)) static void bnum_mul(bnum bn, bnum a, bnum b)
|
||||||
|
{
|
||||||
|
// No error handling
|
||||||
|
BN_CTX *ctx = BN_CTX_new();
|
||||||
|
BN_mul(bn, a, b, ctx);
|
||||||
|
BN_CTX_free(ctx);
|
||||||
|
}
|
||||||
|
__attribute__((unused)) static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
||||||
|
{
|
||||||
|
// No error handling
|
||||||
|
BN_CTX *ctx = BN_CTX_new();
|
||||||
|
BN_mod_exp(bn, y, q, p, ctx);
|
||||||
|
BN_CTX_free(ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------- SHARED HASHING HELPERS ------------------------ */
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENSSL
|
||||||
|
enum hash_alg
|
||||||
|
{
|
||||||
|
HASH_SHA1,
|
||||||
|
HASH_SHA224,
|
||||||
|
HASH_SHA256,
|
||||||
|
HASH_SHA384,
|
||||||
|
HASH_SHA512,
|
||||||
|
};
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
enum hash_alg
|
||||||
|
{
|
||||||
|
HASH_SHA1 = GCRY_MD_SHA1,
|
||||||
|
HASH_SHA224 = GCRY_MD_SHA224,
|
||||||
|
HASH_SHA256 = GCRY_MD_SHA256,
|
||||||
|
HASH_SHA384 = GCRY_MD_SHA384,
|
||||||
|
HASH_SHA512 = GCRY_MD_SHA512,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_OPENSSL
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
SHA_CTX sha;
|
||||||
|
SHA256_CTX sha256;
|
||||||
|
SHA512_CTX sha512;
|
||||||
|
} HashCTX;
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
typedef gcry_md_hd_t HashCTX;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_init(enum hash_alg alg, HashCTX *c);
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_update(enum hash_alg alg, HashCTX *c, const void *data, size_t len);
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_final(enum hash_alg alg, HashCTX *c, unsigned char *md);
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
hash(enum hash_alg alg, const unsigned char *d, size_t n, unsigned char *md);
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_length(enum hash_alg alg);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
bnum
|
||||||
|
H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_hash_n(enum hash_alg alg, HashCTX *ctx, const bnum n);
|
||||||
|
|
||||||
|
void
|
||||||
|
hash_num(enum hash_alg alg, const bnum n, unsigned char *dest);
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------- OTHER HELPERS -------------------------------*/
|
||||||
|
|
||||||
|
#ifdef DEBUG_PAIR
|
||||||
|
void
|
||||||
|
hexdump(const char *msg, uint8_t *mem, size_t len);
|
||||||
|
#endif
|
|
@ -0,0 +1,578 @@
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
* so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h> // for isprint()
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
|
#include "pair.h"
|
||||||
|
#include "pair-internal.h"
|
||||||
|
|
||||||
|
extern struct pair_definition pair_fruit;
|
||||||
|
extern struct pair_definition pair_homekit_normal;
|
||||||
|
extern struct pair_definition pair_homekit_transient;
|
||||||
|
|
||||||
|
// Must be in sync with enum pair_type
|
||||||
|
static struct pair_definition *pair[] = {
|
||||||
|
&pair_fruit,
|
||||||
|
&pair_homekit_normal,
|
||||||
|
&pair_homekit_transient,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------- SHARED HASHING HELPERS ------------------------ */
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_init(enum hash_alg alg, HashCTX *c)
|
||||||
|
{
|
||||||
|
#if CONFIG_OPENSSL
|
||||||
|
switch (alg)
|
||||||
|
{
|
||||||
|
case HASH_SHA1 : return SHA1_Init(&c->sha);
|
||||||
|
case HASH_SHA224: return SHA224_Init(&c->sha256);
|
||||||
|
case HASH_SHA256: return SHA256_Init(&c->sha256);
|
||||||
|
case HASH_SHA384: return SHA384_Init(&c->sha512);
|
||||||
|
case HASH_SHA512: return SHA512_Init(&c->sha512);
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
gcry_error_t err;
|
||||||
|
|
||||||
|
err = gcry_md_open(c, alg, 0);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_update(enum hash_alg alg, HashCTX *c, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
#if CONFIG_OPENSSL
|
||||||
|
switch (alg)
|
||||||
|
{
|
||||||
|
case HASH_SHA1 : return SHA1_Update(&c->sha, data, len);
|
||||||
|
case HASH_SHA224: return SHA224_Update(&c->sha256, data, len);
|
||||||
|
case HASH_SHA256: return SHA256_Update(&c->sha256, data, len);
|
||||||
|
case HASH_SHA384: return SHA384_Update(&c->sha512, data, len);
|
||||||
|
case HASH_SHA512: return SHA512_Update(&c->sha512, data, len);
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
gcry_md_write(*c, data, len);
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_final(enum hash_alg alg, HashCTX *c, unsigned char *md)
|
||||||
|
{
|
||||||
|
#if CONFIG_OPENSSL
|
||||||
|
switch (alg)
|
||||||
|
{
|
||||||
|
case HASH_SHA1 : return SHA1_Final(md, &c->sha);
|
||||||
|
case HASH_SHA224: return SHA224_Final(md, &c->sha256);
|
||||||
|
case HASH_SHA256: return SHA256_Final(md, &c->sha256);
|
||||||
|
case HASH_SHA384: return SHA384_Final(md, &c->sha512);
|
||||||
|
case HASH_SHA512: return SHA512_Final(md, &c->sha512);
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
unsigned char *buf = gcry_md_read(*c, alg);
|
||||||
|
if (!buf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(md, buf, gcry_md_get_algo_dlen(alg));
|
||||||
|
gcry_md_close(*c);
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
hash(enum hash_alg alg, const unsigned char *d, size_t n, unsigned char *md)
|
||||||
|
{
|
||||||
|
#if CONFIG_OPENSSL
|
||||||
|
switch (alg)
|
||||||
|
{
|
||||||
|
case HASH_SHA1 : return SHA1(d, n, md);
|
||||||
|
case HASH_SHA224: return SHA224(d, n, md);
|
||||||
|
case HASH_SHA256: return SHA256(d, n, md);
|
||||||
|
case HASH_SHA384: return SHA384(d, n, md);
|
||||||
|
case HASH_SHA512: return SHA512(d, n, md);
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
gcry_md_hash_buffer(alg, md, d, n);
|
||||||
|
return md;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_length(enum hash_alg alg)
|
||||||
|
{
|
||||||
|
#if CONFIG_OPENSSL
|
||||||
|
switch (alg)
|
||||||
|
{
|
||||||
|
case HASH_SHA1 : return SHA_DIGEST_LENGTH;
|
||||||
|
case HASH_SHA224: return SHA224_DIGEST_LENGTH;
|
||||||
|
case HASH_SHA256: return SHA256_DIGEST_LENGTH;
|
||||||
|
case HASH_SHA384: return SHA384_DIGEST_LENGTH;
|
||||||
|
case HASH_SHA512: return SHA512_DIGEST_LENGTH;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
#elif CONFIG_GCRYPT
|
||||||
|
return gcry_md_get_algo_dlen(alg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_len, const unsigned char *m2, int m2_len)
|
||||||
|
{
|
||||||
|
HashCTX ctx;
|
||||||
|
|
||||||
|
hash_init(alg, &ctx);
|
||||||
|
hash_update(alg, &ctx, m1, m1_len);
|
||||||
|
hash_update(alg, &ctx, m2, m2_len);
|
||||||
|
return hash_final(alg, &ctx, md);
|
||||||
|
}
|
||||||
|
|
||||||
|
bnum
|
||||||
|
H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
if ((len_n2 < 1) || (len_n2 > len_n1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bin = calloc( 1, nbytes );
|
||||||
|
|
||||||
|
bnum_bn2bin(n1, bin, len_n1);
|
||||||
|
bnum_bn2bin(n2, bin + nbytes - len_n2, len_n2);
|
||||||
|
hash( alg, bin, nbytes, buff );
|
||||||
|
free(bin);
|
||||||
|
bnum_bin2bn(bn, buff, hash_length(alg));
|
||||||
|
return bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bnum
|
||||||
|
H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes)
|
||||||
|
{
|
||||||
|
bnum bn;
|
||||||
|
unsigned char buff[SHA512_DIGEST_LENGTH];
|
||||||
|
int len_n = bnum_num_bytes(n);
|
||||||
|
int nbytes = len_n + 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 );
|
||||||
|
free(bin);
|
||||||
|
bnum_bin2bn(bn, buff, hash_length(alg));
|
||||||
|
return bn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_hash_n(enum hash_alg alg, HashCTX *ctx, const bnum n)
|
||||||
|
{
|
||||||
|
unsigned long len = bnum_num_bytes(n);
|
||||||
|
unsigned char *n_bytes = malloc(len);
|
||||||
|
|
||||||
|
bnum_bn2bin(n, n_bytes, len);
|
||||||
|
hash_update(alg, ctx, n_bytes, len);
|
||||||
|
free(n_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hash_num(enum hash_alg alg, const bnum n, unsigned char *dest)
|
||||||
|
{
|
||||||
|
int nbytes = bnum_num_bytes(n);
|
||||||
|
unsigned char *bin = malloc(nbytes);
|
||||||
|
|
||||||
|
bnum_bn2bin(n, bin, nbytes);
|
||||||
|
hash( alg, bin, nbytes, dest );
|
||||||
|
free(bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------- OTHER HELPERS -------------------------------*/
|
||||||
|
|
||||||
|
#ifdef DEBUG_PAIR
|
||||||
|
void
|
||||||
|
hexdump(const char *msg, uint8_t *mem, size_t len)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int hexdump_cols = 16;
|
||||||
|
|
||||||
|
if (msg)
|
||||||
|
printf("%s", msg);
|
||||||
|
|
||||||
|
for (i = 0; i < len + ((len % hexdump_cols) ? (hexdump_cols - len % hexdump_cols) : 0); i++)
|
||||||
|
{
|
||||||
|
if(i % hexdump_cols == 0)
|
||||||
|
printf("0x%06x: ", i);
|
||||||
|
|
||||||
|
if (i < len)
|
||||||
|
printf("%02x ", 0xFF & ((char*)mem)[i]);
|
||||||
|
else
|
||||||
|
printf(" ");
|
||||||
|
|
||||||
|
if (i % hexdump_cols == (hexdump_cols - 1))
|
||||||
|
{
|
||||||
|
for (j = i - (hexdump_cols - 1); j <= i; j++)
|
||||||
|
{
|
||||||
|
if (j >= len)
|
||||||
|
putchar(' ');
|
||||||
|
else if (isprint(((char*)mem)[j]))
|
||||||
|
putchar(0xFF & ((char*)mem)[j]);
|
||||||
|
else
|
||||||
|
putchar('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------- API -----------------------------------*/
|
||||||
|
|
||||||
|
struct pair_setup_context *
|
||||||
|
pair_setup_new(enum pair_type type, const char *pin, const char *device_id)
|
||||||
|
{
|
||||||
|
if (!pair[type]->pair_setup_new)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return pair[type]->pair_setup_new(pair[type], pin, device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pair_setup_free(struct pair_setup_context *sctx)
|
||||||
|
{
|
||||||
|
if (!sctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sctx->type->pair_setup_free)
|
||||||
|
return;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_free(sctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
pair_setup_errmsg(struct pair_setup_context *sctx)
|
||||||
|
{
|
||||||
|
return sctx->errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_setup_request1(uint32_t *len, struct pair_setup_context *sctx)
|
||||||
|
{
|
||||||
|
if (!sctx->type->pair_setup_request1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_request1(len, sctx);
|
||||||
|
|
||||||
|
if (!sctx->type->pair_setup_request1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_request1(len, sctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_setup_request2(uint32_t *len, struct pair_setup_context *sctx)
|
||||||
|
{
|
||||||
|
if (!sctx->type->pair_setup_request2)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_request2(len, sctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_setup_request3(uint32_t *len, struct pair_setup_context *sctx)
|
||||||
|
{
|
||||||
|
if (!sctx->type->pair_setup_request3)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_request3(len, sctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
||||||
|
{
|
||||||
|
if (!sctx->type->pair_setup_response1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_response1(sctx, data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
||||||
|
{
|
||||||
|
if (!sctx->type->pair_setup_response2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return sctx->type->pair_setup_response2(sctx, data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
||||||
|
{
|
||||||
|
if (!sctx->type->pair_setup_response3)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (sctx->type->pair_setup_response3(sctx, data, data_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)
|
||||||
|
{
|
||||||
|
const uint8_t *out_key;
|
||||||
|
size_t out_len;
|
||||||
|
char *ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!sctx->setup_is_completed)
|
||||||
|
{
|
||||||
|
sctx->errmsg = "Setup result: The pair setup has not been completed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sctx->type->pair_setup_result)
|
||||||
|
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))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct pair_verify_context *
|
||||||
|
pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id)
|
||||||
|
{
|
||||||
|
struct pair_verify_context *vctx;
|
||||||
|
char hex[] = { 0, 0, 0 };
|
||||||
|
size_t hexkey_len;
|
||||||
|
const char *ptr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (sodium_init() == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!hexkey)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
hexkey_len = strlen(hexkey);
|
||||||
|
|
||||||
|
if (hexkey_len != 2 * sizeof(vctx->client_private_key))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (device_id && strlen(device_id) != 16)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
vctx = calloc(1, sizeof(struct pair_verify_context));
|
||||||
|
if (!vctx)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
vctx->type = pair[type];
|
||||||
|
|
||||||
|
if (device_id)
|
||||||
|
memcpy(vctx->device_id, device_id, strlen(device_id));
|
||||||
|
|
||||||
|
ptr = hexkey;
|
||||||
|
for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2)
|
||||||
|
{
|
||||||
|
hex[0] = ptr[0];
|
||||||
|
hex[1] = ptr[1];
|
||||||
|
vctx->client_private_key[i] = strtol(hex, NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = hexkey + hexkey_len - 2 * sizeof(vctx->client_public_key);
|
||||||
|
for (i = 0; i < sizeof(vctx->client_public_key); i++, ptr+=2)
|
||||||
|
{
|
||||||
|
hex[0] = ptr[0];
|
||||||
|
hex[1] = ptr[1];
|
||||||
|
vctx->client_public_key[i] = strtol(hex, NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pair_verify_free(struct pair_verify_context *vctx)
|
||||||
|
{
|
||||||
|
if (!vctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(vctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
pair_verify_errmsg(struct pair_verify_context *vctx)
|
||||||
|
{
|
||||||
|
return vctx->errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_verify_request1(uint32_t *len, struct pair_verify_context *vctx)
|
||||||
|
{
|
||||||
|
if (!vctx->type->pair_verify_request1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return vctx->type->pair_verify_request1(len, vctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_verify_request2(uint32_t *len, struct pair_verify_context *vctx)
|
||||||
|
{
|
||||||
|
if (!vctx->type->pair_verify_request2)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return vctx->type->pair_verify_request2(len, vctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_len)
|
||||||
|
{
|
||||||
|
if (!vctx->type->pair_verify_response1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return vctx->type->pair_verify_response1(vctx, data, data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_len)
|
||||||
|
{
|
||||||
|
if (!vctx->type->pair_verify_response2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (vctx->type->pair_verify_response2(vctx, data, data_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)
|
||||||
|
{
|
||||||
|
if (!vctx->verify_is_completed)
|
||||||
|
{
|
||||||
|
vctx->errmsg = "Verify result: The pairing verification did not complete";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*shared_secret = vctx->shared_secret;
|
||||||
|
*shared_secret_len = sizeof(vctx->shared_secret);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pair_cipher_context *
|
||||||
|
pair_cipher_new(enum pair_type type, int channel, const uint8_t *shared_secret, size_t shared_secret_len)
|
||||||
|
{
|
||||||
|
if (!pair[type]->pair_cipher_new)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return pair[type]->pair_cipher_new(pair[type], channel, shared_secret, shared_secret_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pair_cipher_free(struct pair_cipher_context *cctx)
|
||||||
|
{
|
||||||
|
if (!cctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!cctx->type->pair_cipher_free)
|
||||||
|
return;
|
||||||
|
|
||||||
|
return cctx->type->pair_cipher_free(cctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
pair_cipher_errmsg(struct pair_cipher_context *cctx)
|
||||||
|
{
|
||||||
|
return cctx->errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx)
|
||||||
|
{
|
||||||
|
if (!cctx->type->pair_encrypt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return cctx->type->pair_encrypt(ciphertext, ciphertext_len, plaintext, plaintext_len, cctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx)
|
||||||
|
{
|
||||||
|
if (!cctx->type->pair_decrypt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return cctx->type->pair_decrypt(plaintext, plaintext_len, ciphertext, ciphertext_len, cctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pair_encrypt_rollback(struct pair_cipher_context *cctx)
|
||||||
|
{
|
||||||
|
cctx->encryption_counter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pair_decrypt_rollback(struct pair_cipher_context *cctx)
|
||||||
|
{
|
||||||
|
cctx->decryption_counter--;
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
#ifndef __PAIR_AP_H__
|
||||||
|
#define __PAIR_AP_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum pair_type
|
||||||
|
{
|
||||||
|
// This is the pairing type required for Apple TV device verification, which
|
||||||
|
// became mandatory with tvOS 10.2.
|
||||||
|
PAIR_FRUIT,
|
||||||
|
// This is the Homekit type required for AirPlay 2 with both PIN setup and
|
||||||
|
// verification
|
||||||
|
PAIR_HOMEKIT_NORMAL,
|
||||||
|
// Same as normal except PIN is fixed to 3939 and stops after setup step 2,
|
||||||
|
// when session key is established
|
||||||
|
PAIR_HOMEKIT_TRANSIENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pair_setup_context;
|
||||||
|
struct pair_verify_context;
|
||||||
|
struct pair_cipher_context;
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
struct pair_setup_context *
|
||||||
|
pair_setup_new(enum pair_type type, const char *pin, const char *device_id);
|
||||||
|
void
|
||||||
|
pair_setup_free(struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
/* Returns last error message
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
pair_setup_errmsg(struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_setup_request1(uint32_t *len, struct pair_setup_context *sctx);
|
||||||
|
uint8_t *
|
||||||
|
pair_setup_request2(uint32_t *len, struct pair_setup_context *sctx);
|
||||||
|
uint8_t *
|
||||||
|
pair_setup_request3(uint32_t *len, struct pair_setup_context *sctx);
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int
|
||||||
|
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int
|
||||||
|
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, uint32_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);
|
||||||
|
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
struct pair_verify_context *
|
||||||
|
pair_verify_new(enum pair_type type, const char *hexkey, const char *device_id);
|
||||||
|
void
|
||||||
|
pair_verify_free(struct pair_verify_context *vctx);
|
||||||
|
|
||||||
|
/* Returns last error message
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
pair_verify_errmsg(struct pair_verify_context *vctx);
|
||||||
|
|
||||||
|
uint8_t *
|
||||||
|
pair_verify_request1(uint32_t *len, struct pair_verify_context *vctx);
|
||||||
|
uint8_t *
|
||||||
|
pair_verify_request2(uint32_t *len, struct pair_verify_context *vctx);
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_len);
|
||||||
|
int
|
||||||
|
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, uint32_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);
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
* create a ciphering context.
|
||||||
|
*/
|
||||||
|
struct pair_cipher_context *
|
||||||
|
pair_cipher_new(enum pair_type type, int channel, const uint8_t *shared_secret, size_t shared_secret_len);
|
||||||
|
void
|
||||||
|
pair_cipher_free(struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
/* Returns last error message
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
pair_cipher_errmsg(struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
int
|
||||||
|
pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
|
||||||
|
int
|
||||||
|
pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
/* Rolls back the nonce
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
pair_encrypt_rollback(struct pair_cipher_context *cctx);
|
||||||
|
void
|
||||||
|
pair_decrypt_rollback(struct pair_cipher_context *cctx);
|
||||||
|
|
||||||
|
#endif /* !__PAIR_AP_H__ */
|
|
@ -1,22 +1,21 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* The Secure Remote Password 6a implementation included here is by
|
* The Secure Remote Password 6a implementation is adapted from:
|
||||||
* - Tom Cocagne
|
* - Tom Cocagne
|
||||||
* <https://github.com/cocagne/csrp>
|
* <https://github.com/cocagne/csrp>
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
* The MIT License (MIT)
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
* the Software without restriction, including without limitation the rights to
|
* the Software without restriction, including without limitation the rights to
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
* so, subject to the following conditions:
|
* so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
* copies or substantial portions of the Software.
|
* copies or substantial portions of the Software.
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
@ -24,7 +23,7 @@
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -34,74 +33,7 @@
|
||||||
#include <plist/plist.h>
|
#include <plist/plist.h>
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
#include "raop_verification.h"
|
#include "pair-internal.h"
|
||||||
|
|
||||||
#define CONFIG_GCRYPT 1
|
|
||||||
|
|
||||||
/* -------------------- GCRYPT AND OPENSSL COMPABILITY --------------------- */
|
|
||||||
/* partly borrowed from ffmpeg (rtmpdh.c) */
|
|
||||||
|
|
||||||
#if CONFIG_GCRYPT
|
|
||||||
#include <gcrypt.h>
|
|
||||||
#define SHA512_DIGEST_LENGTH 64
|
|
||||||
#define bnum_new(bn) \
|
|
||||||
do { \
|
|
||||||
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { \
|
|
||||||
if (!gcry_check_version("1.5.4")) \
|
|
||||||
abort(); \
|
|
||||||
gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \
|
|
||||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \
|
|
||||||
} \
|
|
||||||
bn = gcry_mpi_new(1); \
|
|
||||||
} while (0)
|
|
||||||
#define bnum_free(bn) gcry_mpi_release(bn)
|
|
||||||
#define bnum_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
|
|
||||||
#define bnum_is_zero(bn) (gcry_mpi_cmp_ui(bn, (unsigned long)0) == 0)
|
|
||||||
#define bnum_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
|
|
||||||
#define bnum_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
|
|
||||||
#define bnum_hex2bn(bn, buf) gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0)
|
|
||||||
#define bnum_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
|
|
||||||
#define bnum_add(bn, a, b) gcry_mpi_add(bn, a, b)
|
|
||||||
#define bnum_sub(bn, a, b) gcry_mpi_sub(bn, a, b)
|
|
||||||
#define bnum_mul(bn, a, b) gcry_mpi_mul(bn, a, b)
|
|
||||||
typedef gcry_mpi_t bnum;
|
|
||||||
static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
|
||||||
{
|
|
||||||
gcry_mpi_powm(bn, y, q, p);
|
|
||||||
}
|
|
||||||
#elif CONFIG_OPENSSL
|
|
||||||
#include <openssl/crypto.h>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#define bnum_new(bn) bn = BN_new()
|
|
||||||
#define bnum_free(bn) BN_free(bn)
|
|
||||||
#define bnum_num_bytes(bn) BN_num_bytes(bn)
|
|
||||||
#define bnum_is_zero(bn) BN_is_zero(bn)
|
|
||||||
#define bnum_bn2bin(bn, buf, len) BN_bn2bin(bn, buf)
|
|
||||||
#define bnum_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0)
|
|
||||||
#define bnum_hex2bn(bn, buf) BN_hex2bn(&bn, buf)
|
|
||||||
#define bnum_random(bn, num_bits) BN_rand(bn, num_bits, 0, 0)
|
|
||||||
#define bnum_add(bn, a, b) BN_add(bn, a, b)
|
|
||||||
#define bnum_sub(bn, a, b) BN_sub(bn, a, b)
|
|
||||||
typedef BIGNUM* bnum;
|
|
||||||
static void bnum_mul(bnum bn, bnum a, bnum b)
|
|
||||||
{
|
|
||||||
// No error handling
|
|
||||||
BN_CTX *ctx = BN_CTX_new();
|
|
||||||
BN_mul(bn, a, b, ctx);
|
|
||||||
BN_CTX_free(ctx);
|
|
||||||
}
|
|
||||||
static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
|
||||||
{
|
|
||||||
// No error handling
|
|
||||||
BN_CTX *ctx = BN_CTX_new();
|
|
||||||
BN_mod_exp(bn, y, q, p, ctx);
|
|
||||||
BN_CTX_free(ctx);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------- DEFINES ETC ------------------------------- */
|
/* ----------------------------- DEFINES ETC ------------------------------- */
|
||||||
|
|
||||||
|
@ -113,74 +45,6 @@ static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
|
||||||
#define AES_VERIFY_KEY "Pair-Verify-AES-Key"
|
#define AES_VERIFY_KEY "Pair-Verify-AES-Key"
|
||||||
#define AES_VERIFY_IV "Pair-Verify-AES-IV"
|
#define AES_VERIFY_IV "Pair-Verify-AES-IV"
|
||||||
|
|
||||||
#ifdef CONFIG_OPENSSL
|
|
||||||
enum hash_alg
|
|
||||||
{
|
|
||||||
HASH_SHA1,
|
|
||||||
HASH_SHA224,
|
|
||||||
HASH_SHA256,
|
|
||||||
HASH_SHA384,
|
|
||||||
HASH_SHA512,
|
|
||||||
};
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
enum hash_alg
|
|
||||||
{
|
|
||||||
HASH_SHA1 = GCRY_MD_SHA1,
|
|
||||||
HASH_SHA224 = GCRY_MD_SHA224,
|
|
||||||
HASH_SHA256 = GCRY_MD_SHA256,
|
|
||||||
HASH_SHA384 = GCRY_MD_SHA384,
|
|
||||||
HASH_SHA512 = GCRY_MD_SHA512,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct verification_setup_context
|
|
||||||
{
|
|
||||||
struct SRPUser *user;
|
|
||||||
|
|
||||||
char pin[4];
|
|
||||||
|
|
||||||
const uint8_t *pkA;
|
|
||||||
int pkA_len;
|
|
||||||
|
|
||||||
uint8_t *pkB;
|
|
||||||
uint64_t pkB_len;
|
|
||||||
|
|
||||||
const uint8_t *M1;
|
|
||||||
int M1_len;
|
|
||||||
|
|
||||||
uint8_t *M2;
|
|
||||||
uint64_t M2_len;
|
|
||||||
|
|
||||||
uint8_t *salt;
|
|
||||||
uint64_t salt_len;
|
|
||||||
uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
|
|
||||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
|
||||||
// Hex-formatet concatenation of public + private, 0-terminated
|
|
||||||
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
|
|
||||||
|
|
||||||
// We don't actually use the server's epk and authtag for anything
|
|
||||||
uint8_t *epk;
|
|
||||||
uint64_t epk_len;
|
|
||||||
uint8_t *authtag;
|
|
||||||
uint64_t authtag_len;
|
|
||||||
|
|
||||||
const char *errmsg;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct verification_verify_context
|
|
||||||
{
|
|
||||||
uint8_t server_eph_public_key[32];
|
|
||||||
uint8_t server_public_key[64];
|
|
||||||
|
|
||||||
uint8_t client_public_key[crypto_sign_PUBLICKEYBYTES];
|
|
||||||
uint8_t client_private_key[crypto_sign_SECRETKEYBYTES];
|
|
||||||
|
|
||||||
uint8_t client_eph_public_key[32];
|
|
||||||
uint8_t client_eph_private_key[32];
|
|
||||||
|
|
||||||
const char *errmsg;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------------------- SRP ---------------------------------- */
|
/* ---------------------------------- SRP ---------------------------------- */
|
||||||
|
|
||||||
|
@ -196,40 +60,29 @@ typedef struct
|
||||||
bnum g;
|
bnum g;
|
||||||
} NGConstant;
|
} NGConstant;
|
||||||
|
|
||||||
#if CONFIG_OPENSSL
|
|
||||||
typedef union
|
|
||||||
{
|
|
||||||
SHA_CTX sha;
|
|
||||||
SHA256_CTX sha256;
|
|
||||||
SHA512_CTX sha512;
|
|
||||||
} HashCTX;
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
typedef gcry_md_hd_t HashCTX;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct SRPUser
|
struct SRPUser
|
||||||
{
|
{
|
||||||
enum hash_alg alg;
|
enum hash_alg alg;
|
||||||
NGConstant *ng;
|
NGConstant *ng;
|
||||||
|
|
||||||
bnum a;
|
bnum a;
|
||||||
bnum A;
|
bnum A;
|
||||||
bnum S;
|
bnum S;
|
||||||
|
|
||||||
const unsigned char *bytes_A;
|
const unsigned char *bytes_A;
|
||||||
int authenticated;
|
int authenticated;
|
||||||
|
|
||||||
const char *username;
|
char *username;
|
||||||
const unsigned char *password;
|
unsigned char *password;
|
||||||
int password_len;
|
int password_len;
|
||||||
|
|
||||||
unsigned char M [SHA512_DIGEST_LENGTH];
|
unsigned char M [SHA512_DIGEST_LENGTH];
|
||||||
unsigned char H_AMK [SHA512_DIGEST_LENGTH];
|
unsigned char H_AMK [SHA512_DIGEST_LENGTH];
|
||||||
unsigned char session_key [2 * SHA512_DIGEST_LENGTH]; // See hash_session_key()
|
unsigned char session_key [2 * SHA512_DIGEST_LENGTH]; // See hash_session_key()
|
||||||
int session_key_len;
|
int session_key_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NGHex
|
struct NGHex
|
||||||
{
|
{
|
||||||
const char *n_hex;
|
const char *n_hex;
|
||||||
const char *g_hex;
|
const char *g_hex;
|
||||||
|
@ -263,10 +116,10 @@ new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex)
|
||||||
n_hex = global_Ng_constants[ ng_type ].n_hex;
|
n_hex = global_Ng_constants[ ng_type ].n_hex;
|
||||||
g_hex = global_Ng_constants[ ng_type ].g_hex;
|
g_hex = global_Ng_constants[ ng_type ].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);
|
||||||
|
|
||||||
return ng;
|
return ng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,167 +134,6 @@ free_ng(NGConstant * ng)
|
||||||
free(ng);
|
free(ng);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
hash_init(enum hash_alg alg, HashCTX *c)
|
|
||||||
{
|
|
||||||
#if CONFIG_OPENSSL
|
|
||||||
switch (alg)
|
|
||||||
{
|
|
||||||
case HASH_SHA1 : return SHA1_Init(&c->sha);
|
|
||||||
case HASH_SHA224: return SHA224_Init(&c->sha256);
|
|
||||||
case HASH_SHA256: return SHA256_Init(&c->sha256);
|
|
||||||
case HASH_SHA384: return SHA384_Init(&c->sha512);
|
|
||||||
case HASH_SHA512: return SHA512_Init(&c->sha512);
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
gcry_error_t err;
|
|
||||||
|
|
||||||
err = gcry_md_open(c, alg, 0);
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
hash_update(enum hash_alg alg, HashCTX *c, const void *data, size_t len)
|
|
||||||
{
|
|
||||||
#if CONFIG_OPENSSL
|
|
||||||
switch (alg)
|
|
||||||
{
|
|
||||||
case HASH_SHA1 : return SHA1_Update(&c->sha, data, len);
|
|
||||||
case HASH_SHA224: return SHA224_Update(&c->sha256, data, len);
|
|
||||||
case HASH_SHA256: return SHA256_Update(&c->sha256, data, len);
|
|
||||||
case HASH_SHA384: return SHA384_Update(&c->sha512, data, len);
|
|
||||||
case HASH_SHA512: return SHA512_Update(&c->sha512, data, len);
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
gcry_md_write(*c, data, len);
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
hash_final(enum hash_alg alg, HashCTX *c, unsigned char *md)
|
|
||||||
{
|
|
||||||
#if CONFIG_OPENSSL
|
|
||||||
switch (alg)
|
|
||||||
{
|
|
||||||
case HASH_SHA1 : return SHA1_Final(md, &c->sha);
|
|
||||||
case HASH_SHA224: return SHA224_Final(md, &c->sha256);
|
|
||||||
case HASH_SHA256: return SHA256_Final(md, &c->sha256);
|
|
||||||
case HASH_SHA384: return SHA384_Final(md, &c->sha512);
|
|
||||||
case HASH_SHA512: return SHA512_Final(md, &c->sha512);
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
unsigned char *buf = gcry_md_read(*c, alg);
|
|
||||||
if (!buf)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memcpy(md, buf, gcry_md_get_algo_dlen(alg));
|
|
||||||
gcry_md_close(*c);
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char *
|
|
||||||
hash(enum hash_alg alg, const unsigned char *d, size_t n, unsigned char *md)
|
|
||||||
{
|
|
||||||
#if CONFIG_OPENSSL
|
|
||||||
switch (alg)
|
|
||||||
{
|
|
||||||
case HASH_SHA1 : return SHA1(d, n, md);
|
|
||||||
case HASH_SHA224: return SHA224(d, n, md);
|
|
||||||
case HASH_SHA256: return SHA256(d, n, md);
|
|
||||||
case HASH_SHA384: return SHA384(d, n, md);
|
|
||||||
case HASH_SHA512: return SHA512(d, n, md);
|
|
||||||
default:
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
gcry_md_hash_buffer(alg, md, d, n);
|
|
||||||
return md;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
hash_length(enum hash_alg alg)
|
|
||||||
{
|
|
||||||
#if CONFIG_OPENSSL
|
|
||||||
switch (alg)
|
|
||||||
{
|
|
||||||
case HASH_SHA1 : return SHA_DIGEST_LENGTH;
|
|
||||||
case HASH_SHA224: return SHA224_DIGEST_LENGTH;
|
|
||||||
case HASH_SHA256: return SHA256_DIGEST_LENGTH;
|
|
||||||
case HASH_SHA384: return SHA384_DIGEST_LENGTH;
|
|
||||||
case HASH_SHA512: return SHA512_DIGEST_LENGTH;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
#elif CONFIG_GCRYPT
|
|
||||||
return gcry_md_get_algo_dlen(alg);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
hash_ab(enum hash_alg alg, unsigned char *md, const unsigned char *m1, int m1_len, const unsigned char *m2, int m2_len)
|
|
||||||
{
|
|
||||||
HashCTX ctx;
|
|
||||||
|
|
||||||
hash_init(alg, &ctx);
|
|
||||||
hash_update(alg, &ctx, m1, m1_len);
|
|
||||||
hash_update(alg, &ctx, m2, m2_len);
|
|
||||||
return hash_final(alg, &ctx, md);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bnum
|
|
||||||
H_nn_pad(enum hash_alg alg, const bnum n1, const bnum n2)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
if ((len_n2 < 1) || (len_n2 > len_n1))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
bin = calloc( 1, nbytes );
|
|
||||||
|
|
||||||
bnum_bn2bin(n1, bin, len_n1);
|
|
||||||
bnum_bn2bin(n2, bin + nbytes - len_n2, len_n2);
|
|
||||||
hash( alg, bin, nbytes, buff );
|
|
||||||
free(bin);
|
|
||||||
bnum_bin2bn(bn, buff, hash_length(alg));
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bnum
|
|
||||||
H_ns(enum hash_alg alg, const bnum n, const unsigned char *bytes, int len_bytes)
|
|
||||||
{
|
|
||||||
bnum bn;
|
|
||||||
unsigned char buff[SHA512_DIGEST_LENGTH];
|
|
||||||
int len_n = bnum_num_bytes(n);
|
|
||||||
int nbytes = len_n + 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 );
|
|
||||||
free(bin);
|
|
||||||
bnum_bin2bn(bn, buff, hash_length(alg));
|
|
||||||
return bn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bnum
|
static bnum
|
||||||
calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsigned char *password, int password_len)
|
calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsigned char *password, int password_len)
|
||||||
{
|
{
|
||||||
|
@ -453,30 +145,10 @@ calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsi
|
||||||
hash_update( alg, &ctx, ":", 1 );
|
hash_update( alg, &ctx, ":", 1 );
|
||||||
hash_update( alg, &ctx, password, password_len );
|
hash_update( alg, &ctx, password, password_len );
|
||||||
hash_final( alg, &ctx, ucp_hash );
|
hash_final( alg, &ctx, ucp_hash );
|
||||||
|
|
||||||
return H_ns( alg, salt, ucp_hash, hash_length(alg) );
|
return H_ns( alg, salt, ucp_hash, hash_length(alg) );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
update_hash_n(enum hash_alg alg, HashCTX *ctx, const bnum n)
|
|
||||||
{
|
|
||||||
unsigned long len = bnum_num_bytes(n);
|
|
||||||
unsigned char *n_bytes = malloc(len);
|
|
||||||
bnum_bn2bin(n, n_bytes, len);
|
|
||||||
hash_update(alg, ctx, n_bytes, len);
|
|
||||||
free(n_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
hash_num(enum hash_alg alg, const bnum n, unsigned char *dest)
|
|
||||||
{
|
|
||||||
int nbytes = bnum_num_bytes(n);
|
|
||||||
unsigned char *bin = malloc(nbytes);
|
|
||||||
bnum_bn2bin(n, bin, nbytes);
|
|
||||||
hash( alg, bin, nbytes, dest );
|
|
||||||
free(bin);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
hash_session_key(enum hash_alg alg, const bnum n, unsigned char *dest)
|
hash_session_key(enum hash_alg alg, const bnum n, unsigned char *dest)
|
||||||
{
|
{
|
||||||
|
@ -508,24 +180,24 @@ calculate_M(enum hash_alg alg, NGConstant *ng, unsigned char *dest, const char *
|
||||||
HashCTX ctx;
|
HashCTX ctx;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int hash_len = hash_length(alg);
|
int hash_len = hash_length(alg);
|
||||||
|
|
||||||
hash_num( alg, ng->N, H_N );
|
hash_num( alg, ng->N, H_N );
|
||||||
hash_num( alg, ng->g, H_g );
|
hash_num( alg, ng->g, H_g );
|
||||||
|
|
||||||
hash(alg, (const unsigned char *)I, strlen(I), H_I);
|
hash(alg, (const unsigned char *)I, strlen(I), H_I);
|
||||||
|
|
||||||
for (i = 0; i < hash_len; i++)
|
for (i=0; i < hash_len; i++ )
|
||||||
H_xor[i] = H_N[i] ^ H_g[i];
|
H_xor[i] = H_N[i] ^ H_g[i];
|
||||||
|
|
||||||
hash_init( alg, &ctx );
|
hash_init( alg, &ctx );
|
||||||
|
|
||||||
hash_update( alg, &ctx, H_xor, hash_len );
|
hash_update( alg, &ctx, H_xor, hash_len );
|
||||||
hash_update( alg, &ctx, H_I, hash_len );
|
hash_update( alg, &ctx, H_I, hash_len );
|
||||||
update_hash_n( alg, &ctx, s );
|
update_hash_n( alg, &ctx, s );
|
||||||
update_hash_n( alg, &ctx, A );
|
update_hash_n( alg, &ctx, A );
|
||||||
update_hash_n( alg, &ctx, B );
|
update_hash_n( alg, &ctx, B );
|
||||||
hash_update( alg, &ctx, K, K_len );
|
hash_update( alg, &ctx, K, K_len );
|
||||||
|
|
||||||
hash_final( alg, &ctx, dest );
|
hash_final( alg, &ctx, dest );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,18 +205,18 @@ static void
|
||||||
calculate_H_AMK(enum hash_alg alg, unsigned char *dest, const bnum A, const unsigned char * M, const unsigned char * K, int K_len)
|
calculate_H_AMK(enum hash_alg alg, unsigned char *dest, const bnum A, const unsigned char * M, const unsigned char * K, int K_len)
|
||||||
{
|
{
|
||||||
HashCTX ctx;
|
HashCTX ctx;
|
||||||
|
|
||||||
hash_init( alg, &ctx );
|
hash_init( alg, &ctx );
|
||||||
|
|
||||||
update_hash_n( alg, &ctx, A );
|
update_hash_n( alg, &ctx, A );
|
||||||
hash_update( alg, &ctx, M, hash_length(alg) );
|
hash_update( alg, &ctx, M, hash_length(alg) );
|
||||||
hash_update( alg, &ctx, K, K_len );
|
hash_update( alg, &ctx, K, K_len );
|
||||||
|
|
||||||
hash_final( alg, &ctx, dest );
|
hash_final( alg, &ctx, dest );
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct SRPUser *
|
static struct SRPUser *
|
||||||
srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
||||||
const unsigned char *bytes_password, int len_password,
|
const unsigned char *bytes_password, int len_password,
|
||||||
const char *n_hex, const char *g_hex)
|
const char *n_hex, const char *g_hex)
|
||||||
{
|
{
|
||||||
|
@ -556,27 +228,27 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
||||||
|
|
||||||
usr->alg = alg;
|
usr->alg = alg;
|
||||||
usr->ng = new_ng( ng_type, n_hex, g_hex );
|
usr->ng = new_ng( ng_type, n_hex, g_hex );
|
||||||
|
|
||||||
bnum_new(usr->a);
|
bnum_new(usr->a);
|
||||||
bnum_new(usr->A);
|
bnum_new(usr->A);
|
||||||
bnum_new(usr->S);
|
bnum_new(usr->S);
|
||||||
|
|
||||||
if (!usr->ng || !usr->a || !usr->A || !usr->S)
|
if (!usr->ng || !usr->a || !usr->A || !usr->S)
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
|
|
||||||
usr->username = (const char *) malloc(ulen);
|
usr->username = malloc(ulen);
|
||||||
usr->password = (const unsigned char *) malloc(len_password);
|
usr->password = malloc(len_password);
|
||||||
usr->password_len = len_password;
|
usr->password_len = len_password;
|
||||||
|
|
||||||
if (!usr->username || !usr->password)
|
if (!usr->username || !usr->password)
|
||||||
goto err_exit;
|
goto err_exit;
|
||||||
|
|
||||||
memcpy((char *)usr->username, username, ulen);
|
memcpy(usr->username, username, ulen);
|
||||||
memcpy((char *)usr->password, bytes_password, len_password);
|
memcpy(usr->password, bytes_password, len_password);
|
||||||
|
|
||||||
usr->authenticated = 0;
|
usr->authenticated = 0;
|
||||||
usr->bytes_A = 0;
|
usr->bytes_A = 0;
|
||||||
|
|
||||||
return usr;
|
return usr;
|
||||||
|
|
||||||
err_exit:
|
err_exit:
|
||||||
|
@ -586,12 +258,12 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
||||||
bnum_free(usr->a);
|
bnum_free(usr->a);
|
||||||
bnum_free(usr->A);
|
bnum_free(usr->A);
|
||||||
bnum_free(usr->S);
|
bnum_free(usr->S);
|
||||||
if (usr->username)
|
|
||||||
free((void*)usr->username);
|
free(usr->username);
|
||||||
if (usr->password)
|
if (usr->password)
|
||||||
{
|
{
|
||||||
memset((void*)usr->password, 0, usr->password_len);
|
memset(usr->password, 0, usr->password_len);
|
||||||
free((void*)usr->password);
|
free(usr->password);
|
||||||
}
|
}
|
||||||
free(usr);
|
free(usr);
|
||||||
|
|
||||||
|
@ -599,7 +271,7 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
srp_user_delete(struct SRPUser *usr)
|
srp_user_free(struct SRPUser *usr)
|
||||||
{
|
{
|
||||||
if(!usr)
|
if(!usr)
|
||||||
return;
|
return;
|
||||||
|
@ -607,16 +279,14 @@ srp_user_delete(struct SRPUser *usr)
|
||||||
bnum_free(usr->a);
|
bnum_free(usr->a);
|
||||||
bnum_free(usr->A);
|
bnum_free(usr->A);
|
||||||
bnum_free(usr->S);
|
bnum_free(usr->S);
|
||||||
|
|
||||||
free_ng(usr->ng);
|
free_ng(usr->ng);
|
||||||
|
|
||||||
memset((void*)usr->password, 0, usr->password_len);
|
memset(usr->password, 0, usr->password_len);
|
||||||
|
|
||||||
free((char *)usr->username);
|
free(usr->username);
|
||||||
free((char *)usr->password);
|
free(usr->password);
|
||||||
|
free((char *)usr->bytes_A);
|
||||||
if (usr->bytes_A)
|
|
||||||
free( (char *)usr->bytes_A );
|
|
||||||
|
|
||||||
memset(usr, 0, sizeof(*usr));
|
memset(usr, 0, sizeof(*usr));
|
||||||
free(usr);
|
free(usr);
|
||||||
|
@ -643,7 +313,7 @@ srp_user_start_authentication(struct SRPUser *usr, const char **username,
|
||||||
{
|
{
|
||||||
bnum_random(usr->a, 256);
|
bnum_random(usr->a, 256);
|
||||||
bnum_modexp(usr->A, usr->ng->g, usr->a, usr->ng->N);
|
bnum_modexp(usr->A, usr->ng->g, usr->a, usr->ng->N);
|
||||||
|
|
||||||
*len_A = bnum_num_bytes(usr->A);
|
*len_A = bnum_num_bytes(usr->A);
|
||||||
*bytes_A = malloc(*len_A);
|
*bytes_A = malloc(*len_A);
|
||||||
|
|
||||||
|
@ -654,9 +324,9 @@ srp_user_start_authentication(struct SRPUser *usr, const char **username,
|
||||||
*username = 0;
|
*username = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bnum_bn2bin(usr->A, (unsigned char *) *bytes_A, *len_A);
|
bnum_bn2bin(usr->A, (unsigned char *) *bytes_A, *len_A);
|
||||||
|
|
||||||
usr->bytes_A = *bytes_A;
|
usr->bytes_A = *bytes_A;
|
||||||
*username = usr->username;
|
*username = usr->username;
|
||||||
}
|
}
|
||||||
|
@ -694,7 +364,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in
|
||||||
if (!bnum_is_zero(B) && !bnum_is_zero(u))
|
if (!bnum_is_zero(B) && !bnum_is_zero(u))
|
||||||
{
|
{
|
||||||
bnum_modexp(v, usr->ng->g, x, usr->ng->N);
|
bnum_modexp(v, usr->ng->g, x, usr->ng->N);
|
||||||
|
|
||||||
// S = (B - k*(g^x)) ^ (a + ux)
|
// S = (B - k*(g^x)) ^ (a + ux)
|
||||||
bnum_mul(tmp1, u, x);
|
bnum_mul(tmp1, u, x);
|
||||||
bnum_add(tmp2, usr->a, tmp1); // tmp2 = (a + ux)
|
bnum_add(tmp2, usr->a, tmp1); // tmp2 = (a + ux)
|
||||||
|
@ -704,17 +374,19 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in
|
||||||
bnum_modexp(usr->S, tmp1, tmp2, usr->ng->N);
|
bnum_modexp(usr->S, tmp1, tmp2, usr->ng->N);
|
||||||
|
|
||||||
usr->session_key_len = hash_session_key(usr->alg, usr->S, usr->session_key);
|
usr->session_key_len = hash_session_key(usr->alg, usr->S, usr->session_key);
|
||||||
|
|
||||||
calculate_M(usr->alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key, usr->session_key_len);
|
calculate_M(usr->alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key, usr->session_key_len);
|
||||||
calculate_H_AMK(usr->alg, usr->H_AMK, usr->A, usr->M, usr->session_key, usr->session_key_len);
|
calculate_H_AMK(usr->alg, usr->H_AMK, usr->A, usr->M, usr->session_key, usr->session_key_len);
|
||||||
|
|
||||||
*bytes_M = usr->M;
|
*bytes_M = usr->M;
|
||||||
*len_M = hash_length(usr->alg);
|
if (len_M)
|
||||||
|
*len_M = hash_length(usr->alg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*bytes_M = NULL;
|
*bytes_M = NULL;
|
||||||
*len_M = 0;
|
if (len_M)
|
||||||
|
*len_M = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup2:
|
cleanup2:
|
||||||
|
@ -920,32 +592,37 @@ encrypt_ctr(unsigned char *ciphertext, int ciphertext_len,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------------------- API ---------------------------------- */
|
/* -------------------------- IMPLEMENTATION -------------------------------- */
|
||||||
|
|
||||||
struct verification_setup_context *
|
static struct pair_setup_context *
|
||||||
verification_setup_new(const char *pin)
|
pair_setup_new(struct pair_definition *type, const char *pin, const char *device_id)
|
||||||
{
|
{
|
||||||
struct verification_setup_context *sctx;
|
struct pair_setup_context *sctx;
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
if (sodium_init() == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sctx = calloc(1, sizeof(struct verification_setup_context));
|
if (!pin || strlen(pin) < 4)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sctx = calloc(1, sizeof(struct pair_setup_context));
|
||||||
if (!sctx)
|
if (!sctx)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
sctx->type = type;
|
||||||
|
|
||||||
memcpy(sctx->pin, pin, sizeof(sctx->pin));
|
memcpy(sctx->pin, pin, sizeof(sctx->pin));
|
||||||
|
|
||||||
return sctx;
|
return sctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
verification_setup_free(struct verification_setup_context *sctx)
|
pair_setup_free(struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
if (!sctx)
|
if (!sctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
srp_user_delete(sctx->user);
|
srp_user_free(sctx->user);
|
||||||
|
|
||||||
free(sctx->pkB);
|
free(sctx->pkB);
|
||||||
free(sctx->M2);
|
free(sctx->M2);
|
||||||
|
@ -956,14 +633,8 @@ verification_setup_free(struct verification_setup_context *sctx)
|
||||||
free(sctx);
|
free(sctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
static uint8_t *
|
||||||
verification_setup_errmsg(struct verification_setup_context *sctx)
|
pair_setup_request1(uint32_t *len, struct pair_setup_context *sctx)
|
||||||
{
|
|
||||||
return sctx->errmsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *
|
|
||||||
verification_setup_request1(uint32_t *len, struct verification_setup_context *sctx)
|
|
||||||
{
|
{
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t method;
|
plist_t method;
|
||||||
|
@ -985,8 +656,8 @@ verification_setup_request1(uint32_t *len, struct verification_setup_context *sc
|
||||||
return (uint8_t *)data;
|
return (uint8_t *)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *
|
static uint8_t *
|
||||||
verification_setup_request2(uint32_t *len, struct verification_setup_context *sctx)
|
pair_setup_request2(uint32_t *len, struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t pk;
|
plist_t pk;
|
||||||
|
@ -1012,8 +683,8 @@ verification_setup_request2(uint32_t *len, struct verification_setup_context *sc
|
||||||
return (uint8_t *)data;
|
return (uint8_t *)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *
|
static uint8_t *
|
||||||
verification_setup_request3(uint32_t *len, struct verification_setup_context *sctx)
|
pair_setup_request3(uint32_t *len, struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t epk;
|
plist_t epk;
|
||||||
|
@ -1049,7 +720,7 @@ verification_setup_request3(uint32_t *len, struct verification_setup_context *sc
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv[15]++; // Magic
|
iv[15]++; // Nonce?
|
||||||
/*
|
/*
|
||||||
if (iv[15] == 0x00 || iv[15] == 0xff)
|
if (iv[15] == 0x00 || iv[15] == 0xff)
|
||||||
printf("- note that value of last byte is %d!\n", iv[15]);
|
printf("- note that value of last byte is %d!\n", iv[15]);
|
||||||
|
@ -1075,8 +746,8 @@ verification_setup_request3(uint32_t *len, struct verification_setup_context *sc
|
||||||
return (uint8_t *)data;
|
return (uint8_t *)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
verification_setup_response1(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
||||||
{
|
{
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t pk;
|
plist_t pk;
|
||||||
|
@ -1101,8 +772,8 @@ verification_setup_response1(struct verification_setup_context *sctx, const uint
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
verification_setup_response2(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
||||||
{
|
{
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t proof;
|
plist_t proof;
|
||||||
|
@ -1132,8 +803,8 @@ verification_setup_response2(struct verification_setup_context *sctx, const uint
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
verification_setup_response3(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, uint32_t data_len)
|
||||||
{
|
{
|
||||||
plist_t dict;
|
plist_t dict;
|
||||||
plist_t epk;
|
plist_t epk;
|
||||||
|
@ -1163,92 +834,28 @@ verification_setup_response3(struct verification_setup_context *sctx, const uint
|
||||||
|
|
||||||
plist_free(dict);
|
plist_free(dict);
|
||||||
|
|
||||||
|
sctx->setup_is_completed = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
verification_setup_result(const char **authorisation_key, struct verification_setup_context *sctx)
|
pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx)
|
||||||
{
|
{
|
||||||
struct verification_verify_context *vctx;
|
// Last 32 bytes of private key should match public key, but check assumption
|
||||||
char *ptr;
|
if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0)
|
||||||
int i;
|
|
||||||
|
|
||||||
if (sizeof(vctx->client_public_key) != sizeof(sctx->public_key) || sizeof(vctx->client_private_key) != sizeof(sctx->private_key))
|
|
||||||
{
|
{
|
||||||
sctx->errmsg = "Setup result: Bug!";
|
sctx->errmsg = "Pair setup result: Unexpected keys, private key does not match public key";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fills out the auth_key with public + private in hex. It seems that the private
|
*key = sctx->private_key;
|
||||||
// key actually includes the public key (last 32 bytes), so we could in
|
*key_len = sizeof(sctx->private_key);
|
||||||
// principle just export the private key
|
|
||||||
ptr = sctx->auth_key;
|
|
||||||
for (i = 0; i < sizeof(sctx->public_key); i++)
|
|
||||||
ptr += sprintf(ptr, "%02x", sctx->public_key[i]);
|
|
||||||
for (i = 0; i < sizeof(sctx->private_key); i++)
|
|
||||||
ptr += sprintf(ptr, "%02x", sctx->private_key[i]);
|
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
*authorisation_key = sctx->auth_key;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct verification_verify_context *
|
static uint8_t *
|
||||||
verification_verify_new(const char *authorisation_key)
|
pair_verify_request1(uint32_t *len, struct pair_verify_context *vctx)
|
||||||
{
|
|
||||||
struct verification_verify_context *vctx;
|
|
||||||
char hex[] = { 0, 0, 0 };
|
|
||||||
const char *ptr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (sodium_init() == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!authorisation_key)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (strlen(authorisation_key) != 2 * (sizeof(vctx->client_public_key) + sizeof(vctx->client_private_key)))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
vctx = calloc(1, sizeof(struct verification_verify_context));
|
|
||||||
if (!vctx)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ptr = authorisation_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);
|
|
||||||
}
|
|
||||||
for (i = 0; i < sizeof(vctx->client_private_key); i++, ptr+=2)
|
|
||||||
{
|
|
||||||
hex[0] = ptr[0];
|
|
||||||
hex[1] = ptr[1];
|
|
||||||
vctx->client_private_key[i] = strtol(hex, NULL, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
return vctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
verification_verify_free(struct verification_verify_context *vctx)
|
|
||||||
{
|
|
||||||
if (!vctx)
|
|
||||||
return;
|
|
||||||
|
|
||||||
free(vctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
verification_verify_errmsg(struct verification_verify_context *vctx)
|
|
||||||
{
|
|
||||||
return vctx->errmsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *
|
|
||||||
verification_verify_request1(uint32_t *len, struct verification_verify_context *vctx)
|
|
||||||
{
|
{
|
||||||
const uint8_t basepoint[32] = {9};
|
const uint8_t basepoint[32] = {9};
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
|
@ -1276,8 +883,8 @@ verification_verify_request1(uint32_t *len, struct verification_verify_context *
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *
|
static uint8_t *
|
||||||
verification_verify_request2(uint32_t *len, struct verification_verify_context *vctx)
|
pair_verify_request2(uint32_t *len, struct pair_verify_context *vctx)
|
||||||
{
|
{
|
||||||
uint8_t shared_secret[crypto_scalarmult_BYTES];
|
uint8_t shared_secret[crypto_scalarmult_BYTES];
|
||||||
uint8_t key[SHA512_DIGEST_LENGTH];
|
uint8_t key[SHA512_DIGEST_LENGTH];
|
||||||
|
@ -1344,8 +951,8 @@ verification_verify_request2(uint32_t *len, struct verification_verify_context *
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
verification_verify_response1(struct verification_verify_context *vctx, const uint8_t *data, uint32_t data_len)
|
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_len)
|
||||||
{
|
{
|
||||||
uint32_t wanted;
|
uint32_t wanted;
|
||||||
|
|
||||||
|
@ -1361,3 +968,31 @@ verification_verify_response1(struct verification_verify_context *vctx, const ui
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, uint32_t data_len)
|
||||||
|
{
|
||||||
|
// TODO actually check response
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pair_definition pair_fruit =
|
||||||
|
{
|
||||||
|
.pair_setup_new = pair_setup_new,
|
||||||
|
.pair_setup_free = pair_setup_free,
|
||||||
|
.pair_setup_result = pair_setup_result,
|
||||||
|
|
||||||
|
.pair_setup_request1 = pair_setup_request1,
|
||||||
|
.pair_setup_request2 = pair_setup_request2,
|
||||||
|
.pair_setup_request3 = pair_setup_request3,
|
||||||
|
|
||||||
|
.pair_setup_response1 = pair_setup_response1,
|
||||||
|
.pair_setup_response2 = pair_setup_response2,
|
||||||
|
.pair_setup_response3 = pair_setup_response3,
|
||||||
|
|
||||||
|
.pair_verify_request1 = pair_verify_request1,
|
||||||
|
.pair_verify_request2 = pair_verify_request2,
|
||||||
|
|
||||||
|
.pair_verify_response1 = pair_verify_response1,
|
||||||
|
.pair_verify_response2 = pair_verify_response2,
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -66,7 +66,7 @@
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
|
|
||||||
#ifdef RAOP_VERIFICATION
|
#ifdef RAOP_VERIFICATION
|
||||||
#include "raop_verification.h"
|
#include "pair.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ALAC_HEADER_LEN 3
|
#define ALAC_HEADER_LEN 3
|
||||||
|
@ -223,9 +223,9 @@ struct raop_session
|
||||||
unsigned short timing_port; // ATV4 has this set to 0, but it is not used by forked-daapd anyway
|
unsigned short timing_port; // ATV4 has this set to 0, but it is not used by forked-daapd anyway
|
||||||
|
|
||||||
#ifdef RAOP_VERIFICATION
|
#ifdef RAOP_VERIFICATION
|
||||||
/* Device verification, see raop_verification.h */
|
/* Device verification, see pair.h */
|
||||||
struct verification_verify_context *verification_verify_ctx;
|
struct pair_verify_context *pair_verify_ctx;
|
||||||
struct verification_setup_context *verification_setup_ctx;
|
struct pair_setup_context *pair_setup_ctx;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int server_fd;
|
int server_fd;
|
||||||
|
@ -3989,7 +3989,7 @@ raop_cb_startup_options(struct evrtsp_request *req, void *arg)
|
||||||
|
|
||||||
#ifdef RAOP_VERIFICATION
|
#ifdef RAOP_VERIFICATION
|
||||||
static int
|
static int
|
||||||
raop_verification_response_process(int step, struct evrtsp_request *req, struct raop_session *rs)
|
raop_pair_response_process(int step, struct evrtsp_request *req, struct raop_session *rs)
|
||||||
{
|
{
|
||||||
uint8_t *response;
|
uint8_t *response;
|
||||||
const char *errmsg;
|
const char *errmsg;
|
||||||
|
@ -4016,20 +4016,20 @@ raop_verification_response_process(int step, struct evrtsp_request *req, struct
|
||||||
switch (step)
|
switch (step)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
ret = verification_setup_response1(rs->verification_setup_ctx, response, len);
|
ret = pair_setup_response1(rs->pair_setup_ctx, response, len);
|
||||||
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
|
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ret = verification_setup_response2(rs->verification_setup_ctx, response, len);
|
ret = pair_setup_response2(rs->pair_setup_ctx, response, len);
|
||||||
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
|
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
ret = verification_setup_response3(rs->verification_setup_ctx, response, len);
|
ret = pair_setup_response3(rs->pair_setup_ctx, response, len);
|
||||||
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
|
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
ret = verification_verify_response1(rs->verification_verify_ctx, response, len);
|
ret = pair_verify_response1(rs->pair_verify_ctx, response, len);
|
||||||
errmsg = verification_verify_errmsg(rs->verification_verify_ctx);
|
errmsg = pair_verify_errmsg(rs->pair_verify_ctx);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -4046,7 +4046,7 @@ raop_verification_response_process(int step, struct evrtsp_request *req, struct
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
raop_verification_request_send(int step, struct raop_session *rs, void (*cb)(struct evrtsp_request *, void *))
|
raop_pair_request_send(int step, struct raop_session *rs, void (*cb)(struct evrtsp_request *, void *))
|
||||||
{
|
{
|
||||||
struct evrtsp_request *req;
|
struct evrtsp_request *req;
|
||||||
uint8_t *body;
|
uint8_t *body;
|
||||||
|
@ -4059,32 +4059,32 @@ raop_verification_request_send(int step, struct raop_session *rs, void (*cb)(str
|
||||||
switch (step)
|
switch (step)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
body = verification_setup_request1(&len, rs->verification_setup_ctx);
|
body = pair_setup_request1(&len, rs->pair_setup_ctx);
|
||||||
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
|
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
|
||||||
url = "/pair-setup-pin";
|
url = "/pair-setup-pin";
|
||||||
ctype = "application/x-apple-binary-plist";
|
ctype = "application/x-apple-binary-plist";
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
body = verification_setup_request2(&len, rs->verification_setup_ctx);
|
body = pair_setup_request2(&len, rs->pair_setup_ctx);
|
||||||
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
|
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
|
||||||
url = "/pair-setup-pin";
|
url = "/pair-setup-pin";
|
||||||
ctype = "application/x-apple-binary-plist";
|
ctype = "application/x-apple-binary-plist";
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
body = verification_setup_request3(&len, rs->verification_setup_ctx);
|
body = pair_setup_request3(&len, rs->pair_setup_ctx);
|
||||||
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
|
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
|
||||||
url = "/pair-setup-pin";
|
url = "/pair-setup-pin";
|
||||||
ctype = "application/x-apple-binary-plist";
|
ctype = "application/x-apple-binary-plist";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
body = verification_verify_request1(&len, rs->verification_verify_ctx);
|
body = pair_verify_request1(&len, rs->pair_verify_ctx);
|
||||||
errmsg = verification_verify_errmsg(rs->verification_verify_ctx);
|
errmsg = pair_verify_errmsg(rs->pair_verify_ctx);
|
||||||
url = "/pair-verify";
|
url = "/pair-verify";
|
||||||
ctype = "application/octet-stream";
|
ctype = "application/octet-stream";
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
body = verification_verify_request2(&len, rs->verification_verify_ctx);
|
body = pair_verify_request2(&len, rs->pair_verify_ctx);
|
||||||
errmsg = verification_verify_errmsg(rs->verification_verify_ctx);
|
errmsg = pair_verify_errmsg(rs->pair_verify_ctx);
|
||||||
url = "/pair-verify";
|
url = "/pair-verify";
|
||||||
ctype = "application/octet-stream";
|
ctype = "application/octet-stream";
|
||||||
break;
|
break;
|
||||||
|
@ -4135,15 +4135,15 @@ raop_verification_request_send(int step, struct raop_session *rs, void (*cb)(str
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg)
|
raop_cb_pair_verify_step2(struct evrtsp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
struct raop_session *rs = arg;
|
struct raop_session *rs = arg;
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
verification_verify_free(rs->verification_verify_ctx);
|
pair_verify_free(rs->pair_verify_ctx);
|
||||||
|
|
||||||
ret = raop_verification_response_process(5, req, rs);
|
ret = raop_pair_response_process(5, req, rs);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
device = outputs_device_get(rs->device_id);
|
device = outputs_device_get(rs->device_id);
|
||||||
|
@ -4170,13 +4170,13 @@ raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg)
|
raop_cb_pair_verify_step1(struct evrtsp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
struct raop_session *rs = arg;
|
struct raop_session *rs = arg;
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = raop_verification_response_process(4, req, rs);
|
ret = raop_pair_response_process(4, req, rs);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
device = outputs_device_get(rs->device_id);
|
device = outputs_device_get(rs->device_id);
|
||||||
|
@ -4189,61 +4189,68 @@ raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = raop_verification_request_send(5, rs, raop_cb_verification_verify_step2);
|
ret = raop_pair_request_send(5, rs, raop_cb_pair_verify_step2);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
verification_verify_free(rs->verification_verify_ctx);
|
pair_verify_free(rs->pair_verify_ctx);
|
||||||
rs->verification_verify_ctx = NULL;
|
rs->pair_verify_ctx = NULL;
|
||||||
|
|
||||||
rs->state = RAOP_STATE_PASSWORD;
|
rs->state = RAOP_STATE_PASSWORD;
|
||||||
session_failure(rs);
|
session_failure(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
raop_verification_verify(struct raop_session *rs)
|
raop_pair_verify(struct raop_session *rs)
|
||||||
{
|
{
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
|
const char *auth_key;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
device = outputs_device_get(rs->device_id);
|
device = outputs_device_get(rs->device_id);
|
||||||
if (!device)
|
if (!device)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
CHECK_NULL(L_RAOP, rs->verification_verify_ctx = verification_verify_new(device->auth_key));
|
// Backwards compat - older versions saved the key as concat of public key and
|
||||||
|
// private key, but the pair_verify_new only wants the private key
|
||||||
|
if (strlen(device->auth_key) == (32 + 64) * 2)
|
||||||
|
auth_key = device->auth_key + 32;
|
||||||
|
else
|
||||||
|
auth_key = device->auth_key;
|
||||||
|
|
||||||
ret = raop_verification_request_send(4, rs, raop_cb_verification_verify_step1);
|
CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_FRUIT, auth_key, NULL));
|
||||||
|
|
||||||
|
ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
verification_verify_free(rs->verification_verify_ctx);
|
pair_verify_free(rs->pair_verify_ctx);
|
||||||
rs->verification_verify_ctx = NULL;
|
rs->pair_verify_ctx = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
|
raop_cb_pair_setup_step3(struct evrtsp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
struct raop_session *rs = arg;
|
struct raop_session *rs = arg;
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
const char *authorization_key;
|
const char *authorization_key;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = raop_verification_response_process(3, req, rs);
|
ret = raop_pair_response_process(3, req, rs);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = verification_setup_result(&authorization_key, rs->verification_setup_ctx);
|
ret = pair_setup_result(&authorization_key, NULL, NULL, rs->pair_setup_ctx);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Verification setup result error: %s\n", verification_setup_errmsg(rs->verification_setup_ctx));
|
DPRINTF(E_LOG, L_RAOP, "Verification setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx));
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4263,8 +4270,8 @@ raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
|
||||||
rs->state = RAOP_STATE_STOPPED;
|
rs->state = RAOP_STATE_STOPPED;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
verification_setup_free(rs->verification_setup_ctx);
|
pair_setup_free(rs->pair_setup_ctx);
|
||||||
rs->verification_setup_ctx = NULL;
|
rs->pair_setup_ctx = NULL;
|
||||||
|
|
||||||
// Callback to player with result
|
// Callback to player with result
|
||||||
raop_status(rs);
|
raop_status(rs);
|
||||||
|
@ -4275,62 +4282,62 @@ raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
raop_cb_verification_setup_step2(struct evrtsp_request *req, void *arg)
|
raop_cb_pair_setup_step2(struct evrtsp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
struct raop_session *rs = arg;
|
struct raop_session *rs = arg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = raop_verification_response_process(2, req, rs);
|
ret = raop_pair_response_process(2, req, rs);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = raop_verification_request_send(3, rs, raop_cb_verification_setup_step3);
|
ret = raop_pair_request_send(3, rs, raop_cb_pair_setup_step3);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
verification_setup_free(rs->verification_setup_ctx);
|
pair_setup_free(rs->pair_setup_ctx);
|
||||||
rs->verification_setup_ctx = NULL;
|
rs->pair_setup_ctx = NULL;
|
||||||
session_failure(rs);
|
session_failure(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
raop_cb_verification_setup_step1(struct evrtsp_request *req, void *arg)
|
raop_cb_pair_setup_step1(struct evrtsp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
struct raop_session *rs = arg;
|
struct raop_session *rs = arg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = raop_verification_response_process(1, req, rs);
|
ret = raop_pair_response_process(1, req, rs);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = raop_verification_request_send(2, rs, raop_cb_verification_setup_step2);
|
ret = raop_pair_request_send(2, rs, raop_cb_pair_setup_step2);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
verification_setup_free(rs->verification_setup_ctx);
|
pair_setup_free(rs->pair_setup_ctx);
|
||||||
rs->verification_setup_ctx = NULL;
|
rs->pair_setup_ctx = NULL;
|
||||||
session_failure(rs);
|
session_failure(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
raop_verification_setup(struct raop_session *rs, const char *pin)
|
raop_pair_setup(struct raop_session *rs, const char *pin)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
rs->verification_setup_ctx = verification_setup_new(pin);
|
rs->pair_setup_ctx = pair_setup_new(PAIR_FRUIT, pin, NULL);
|
||||||
if (!rs->verification_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");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = raop_verification_request_send(1, rs, raop_cb_verification_setup_step1);
|
ret = raop_pair_request_send(1, rs, raop_cb_pair_setup_step1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
@ -4339,8 +4346,8 @@ raop_verification_setup(struct raop_session *rs, const char *pin)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
verification_setup_free(rs->verification_setup_ctx);
|
pair_setup_free(rs->pair_setup_ctx);
|
||||||
rs->verification_setup_ctx = NULL;
|
rs->pair_setup_ctx = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4355,7 +4362,7 @@ raop_device_authorize(struct output_device *device, const char *pin, int callbac
|
||||||
if (!rs)
|
if (!rs)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = raop_verification_setup(rs, pin);
|
ret = raop_pair_setup(rs, pin);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Could not send verification setup request to '%s' (address %s)\n", device->name, rs->address);
|
DPRINTF(E_LOG, L_RAOP, "Could not send verification setup request to '%s' (address %s)\n", device->name, rs->address);
|
||||||
|
@ -4368,7 +4375,7 @@ raop_device_authorize(struct output_device *device, const char *pin, int callbac
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static int
|
static int
|
||||||
raop_verification_verify(struct raop_session *rs)
|
raop_pair_verify(struct raop_session *rs)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Device '%s' requires verification, but forked-daapd was built with --disable-verification\n", rs->devname);
|
DPRINTF(E_LOG, L_RAOP, "Device '%s' requires verification, but forked-daapd was built with --disable-verification\n", rs->devname);
|
||||||
|
|
||||||
|
@ -4702,7 +4709,7 @@ raop_device_start_generic(struct output_device *device, int callback_id, bool on
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (device->auth_key)
|
if (device->auth_key)
|
||||||
ret = raop_verification_verify(rs);
|
ret = raop_pair_verify(rs);
|
||||||
else if (device->requires_auth)
|
else if (device->requires_auth)
|
||||||
ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start");
|
ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start");
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
#ifndef __VERIFICATION_H__
|
|
||||||
#define __VERIFICATION_H__
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct verification_setup_context;
|
|
||||||
struct verification_verify_context;
|
|
||||||
|
|
||||||
/* When you have the pin-code (must be 4 bytes), create a new context with this
|
|
||||||
* function and then call verification_setup_request1()
|
|
||||||
*/
|
|
||||||
struct verification_setup_context *
|
|
||||||
verification_setup_new(const char *pin);
|
|
||||||
void
|
|
||||||
verification_setup_free(struct verification_setup_context *sctx);
|
|
||||||
|
|
||||||
/* Returns last error message
|
|
||||||
*/
|
|
||||||
const char *
|
|
||||||
verification_setup_errmsg(struct verification_setup_context *sctx);
|
|
||||||
|
|
||||||
uint8_t *
|
|
||||||
verification_setup_request1(uint32_t *len, struct verification_setup_context *sctx);
|
|
||||||
uint8_t *
|
|
||||||
verification_setup_request2(uint32_t *len, struct verification_setup_context *sctx);
|
|
||||||
uint8_t *
|
|
||||||
verification_setup_request3(uint32_t *len, struct verification_setup_context *sctx);
|
|
||||||
|
|
||||||
int
|
|
||||||
verification_setup_response1(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
|
||||||
int
|
|
||||||
verification_setup_response2(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
|
||||||
int
|
|
||||||
verification_setup_response3(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
|
|
||||||
|
|
||||||
/* Returns a 0-terminated string that is the authorisation key. The caller
|
|
||||||
* should save it and use it later to initialize verification_verify_new().
|
|
||||||
* Note that the pointer becomes invalid when you free sctx.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
verification_setup_result(const char **authorisation_key, struct verification_setup_context *sctx);
|
|
||||||
|
|
||||||
|
|
||||||
/* When you have completed the setup you can extract a key with
|
|
||||||
* verification_setup_result(). Give the string as input to this function to
|
|
||||||
* create a verification context and then call verification_verify_request1()
|
|
||||||
*/
|
|
||||||
struct verification_verify_context *
|
|
||||||
verification_verify_new(const char *authorisation_key);
|
|
||||||
void
|
|
||||||
verification_verify_free(struct verification_verify_context *vctx);
|
|
||||||
|
|
||||||
/* Returns last error message
|
|
||||||
*/
|
|
||||||
const char *
|
|
||||||
verification_verify_errmsg(struct verification_verify_context *vctx);
|
|
||||||
|
|
||||||
uint8_t *
|
|
||||||
verification_verify_request1(uint32_t *len, struct verification_verify_context *vctx);
|
|
||||||
uint8_t *
|
|
||||||
verification_verify_request2(uint32_t *len, struct verification_verify_context *vctx);
|
|
||||||
|
|
||||||
int
|
|
||||||
verification_verify_response1(struct verification_verify_context *vctx, const uint8_t *data, uint32_t data_len);
|
|
||||||
|
|
||||||
#endif /* !__VERIFICATION_H__ */
|
|
Loading…
Reference in New Issue