mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-11 14:30:20 -05:00
[airplay] Update pairing modules (pair_ap 0.2)
This commit is contained in:
@@ -54,7 +54,7 @@
|
||||
#include "transcode.h"
|
||||
#include "outputs.h"
|
||||
|
||||
#include "pair.h"
|
||||
#include "pair_ap/pair.h"
|
||||
|
||||
/* List of TODO's for AirPlay 2
|
||||
*
|
||||
@@ -3025,9 +3025,9 @@ payload_make_pair_generic(int step, struct evrtsp_request *req, struct airplay_s
|
||||
free(body);
|
||||
|
||||
// Required!!
|
||||
if (rs->pair_type == PAIR_HOMEKIT_NORMAL)
|
||||
if (rs->pair_type == PAIR_CLIENT_HOMEKIT_NORMAL)
|
||||
evrtsp_add_header(req->output_headers, "X-Apple-HKP", "3");
|
||||
else if (rs->pair_type == PAIR_HOMEKIT_TRANSIENT)
|
||||
else if (rs->pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT)
|
||||
evrtsp_add_header(req->output_headers, "X-Apple-HKP", "4");
|
||||
|
||||
return 0;
|
||||
@@ -3040,7 +3040,7 @@ payload_make_pair_setup1(struct evrtsp_request *req, struct airplay_session *rs,
|
||||
char device_id_hex[16 + 1];
|
||||
|
||||
if (pin)
|
||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
||||
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||
|
||||
snprintf(device_id_hex, sizeof(device_id_hex), "%016" PRIX64, airplay_device_id);
|
||||
|
||||
@@ -3420,7 +3420,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session
|
||||
// Evaluate what next sequence based on response
|
||||
if (rs->statusflags & AIRPLAY_FLAG_ONE_TIME_PAIRING_REQUIRED)
|
||||
{
|
||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
||||
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||
|
||||
if (!device->auth_key)
|
||||
{
|
||||
@@ -3438,7 +3438,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session
|
||||
device->auth_key = NULL;
|
||||
device->requires_auth = 1;
|
||||
|
||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
||||
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||
rs->state = AIRPLAY_STATE_AUTH;
|
||||
return AIRPLAY_SEQ_PIN_START;
|
||||
}
|
||||
@@ -3449,7 +3449,7 @@ response_handler_info_generic(struct evrtsp_request *req, struct airplay_session
|
||||
return AIRPLAY_SEQ_ABORT;
|
||||
}
|
||||
|
||||
rs->pair_type = PAIR_HOMEKIT_TRANSIENT;
|
||||
rs->pair_type = PAIR_CLIENT_HOMEKIT_TRANSIENT;
|
||||
rs->state = AIRPLAY_STATE_INFO;
|
||||
return AIRPLAY_SEQ_PAIR_TRANSIENT;
|
||||
}
|
||||
@@ -3535,14 +3535,14 @@ response_handler_pair_setup1(struct evrtsp_request *req, struct airplay_session
|
||||
{
|
||||
struct output_device *device;
|
||||
|
||||
if (rs->pair_type == PAIR_HOMEKIT_TRANSIENT && req->response_code == RTSP_CONNECTION_AUTH_REQUIRED)
|
||||
if (rs->pair_type == PAIR_CLIENT_HOMEKIT_TRANSIENT && req->response_code == RTSP_CONNECTION_AUTH_REQUIRED)
|
||||
{
|
||||
device = outputs_device_get(rs->device_id);
|
||||
if (!device)
|
||||
return AIRPLAY_SEQ_ABORT;
|
||||
|
||||
device->requires_auth = 1; // FIXME might be reset by mdns announcement
|
||||
rs->pair_type = PAIR_HOMEKIT_NORMAL;
|
||||
rs->pair_type = PAIR_CLIENT_HOMEKIT_NORMAL;
|
||||
|
||||
return AIRPLAY_SEQ_PIN_START;
|
||||
}
|
||||
@@ -3562,7 +3562,7 @@ response_handler_pair_setup2(struct evrtsp_request *req, struct airplay_session
|
||||
if (seq_type != AIRPLAY_SEQ_CONTINUE)
|
||||
return seq_type;
|
||||
|
||||
if (rs->pair_type != PAIR_HOMEKIT_TRANSIENT)
|
||||
if (rs->pair_type != PAIR_CLIENT_HOMEKIT_TRANSIENT)
|
||||
return seq_type;
|
||||
|
||||
ret = pair_setup_result(NULL, &shared_secret, &shared_secret_len, rs->pair_setup_ctx);
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
#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;
|
||||
|
||||
// For rollback
|
||||
uint64_t encryption_counter_prev;
|
||||
uint64_t decryption_counter_prev;
|
||||
|
||||
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)(size_t *len, struct pair_setup_context *sctx);
|
||||
uint8_t *(*pair_setup_request2)(size_t *len, struct pair_setup_context *sctx);
|
||||
uint8_t *(*pair_setup_request3)(size_t *len, struct pair_setup_context *sctx);
|
||||
|
||||
int (*pair_setup_response1)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||
int (*pair_setup_response2)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||
int (*pair_setup_response3)(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||
|
||||
uint8_t *(*pair_verify_request1)(size_t *len, struct pair_verify_context *vctx);
|
||||
uint8_t *(*pair_verify_request2)(size_t *len, struct pair_verify_context *vctx);
|
||||
|
||||
int (*pair_verify_response1)(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len);
|
||||
int (*pair_verify_response2)(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len);
|
||||
|
||||
struct pair_cipher_context *(*pair_cipher_new)(struct pair_definition *type, int channel, const uint8_t *shared_secret, size_t shared_secret_len);
|
||||
void (*pair_cipher_free)(struct pair_cipher_context *cctx);
|
||||
|
||||
ssize_t (*pair_encrypt)(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
|
||||
ssize_t (*pair_decrypt)(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx);
|
||||
};
|
||||
|
||||
|
||||
/* -------------------- 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
|
||||
@@ -1,578 +0,0 @@
|
||||
/*
|
||||
* 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(size_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(size_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(size_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, size_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, size_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, size_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(size_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(size_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, size_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, size_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;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
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);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
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 = cctx->encryption_counter_prev;
|
||||
}
|
||||
|
||||
void
|
||||
pair_decrypt_rollback(struct pair_cipher_context *cctx)
|
||||
{
|
||||
cctx->decryption_counter = cctx->decryption_counter_prev;
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
#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(size_t *len, struct pair_setup_context *sctx);
|
||||
uint8_t *
|
||||
pair_setup_request2(size_t *len, struct pair_setup_context *sctx);
|
||||
uint8_t *
|
||||
pair_setup_request3(size_t *len, struct pair_setup_context *sctx);
|
||||
|
||||
int
|
||||
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||
int
|
||||
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||
int
|
||||
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len);
|
||||
|
||||
/* 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(size_t *len, struct pair_verify_context *vctx);
|
||||
uint8_t *
|
||||
pair_verify_request2(size_t *len, struct pair_verify_context *vctx);
|
||||
|
||||
int
|
||||
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len);
|
||||
int
|
||||
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* The return value equals length of plaintext that was encrypted, so if the
|
||||
* return value == plaintext_len then everything was encrypted. On error -1 is
|
||||
* returned.
|
||||
*/
|
||||
ssize_t
|
||||
pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
|
||||
|
||||
/* The return value equals length of ciphertext that was decrypted, so if the
|
||||
* return value == ciphertext_len then everything was decrypted. On error -1 is
|
||||
* returned.
|
||||
*/
|
||||
ssize_t
|
||||
pair_decrypt(uint8_t **plaintext, size_t *plaintext_len, uint8_t *ciphertext, size_t ciphertext_len, struct pair_cipher_context *cctx);
|
||||
|
||||
/* 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__ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -64,7 +64,7 @@
|
||||
#include "dmap_common.h"
|
||||
#include "rtp_common.h"
|
||||
#include "outputs.h"
|
||||
#include "pair.h"
|
||||
#include "pair_ap/pair.h"
|
||||
|
||||
#define ALAC_HEADER_LEN 3
|
||||
|
||||
@@ -4198,7 +4198,7 @@ raop_pair_verify(struct raop_session *rs)
|
||||
if (!device)
|
||||
goto error;
|
||||
|
||||
CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_FRUIT, device->auth_key, NULL));
|
||||
CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_CLIENT_FRUIT, device->auth_key, NULL));
|
||||
|
||||
ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1);
|
||||
if (ret < 0)
|
||||
@@ -4307,7 +4307,7 @@ raop_pair_setup(struct raop_session *rs, const char *pin)
|
||||
{
|
||||
int ret;
|
||||
|
||||
rs->pair_setup_ctx = pair_setup_new(PAIR_FRUIT, pin, NULL);
|
||||
rs->pair_setup_ctx = pair_setup_new(PAIR_CLIENT_FRUIT, pin, NULL);
|
||||
if (!rs->pair_setup_ctx)
|
||||
{
|
||||
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n");
|
||||
|
||||
Reference in New Issue
Block a user