[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:
ejurgensen 2020-11-27 22:46:38 +01:00
parent 86f762bb1f
commit 8368ca7686
14 changed files with 7479 additions and 621 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

4429
src/outputs/airplay.c Normal file

File diff suppressed because it is too large Load Diff

240
src/outputs/pair-internal.h Normal file
View File

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

578
src/outputs/pair.c Normal file
View File

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

117
src/outputs/pair.h Normal file
View File

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

View File

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

1885
src/outputs/pair_homekit.c Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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