[airplay] Update pairing modules (pair_ap 0.2)

This commit is contained in:
ejurgensen
2021-03-07 23:01:50 +01:00
parent 6b3573d4f5
commit bbe7526414
10 changed files with 1466 additions and 564 deletions

317
src/pair_ap/pair-internal.h Normal file
View File

@@ -0,0 +1,317 @@
#include <stdint.h>
#include <sodium.h>
struct SRPUser;
struct SRPVerifier;
struct pair_client_setup_context
{
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];
// 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;
};
struct pair_server_setup_context
{
struct SRPVerifier *verifier;
char pin[4];
char device_id[17]; // Incl. zero term
uint8_t *pkA;
uint64_t pkA_len;
uint8_t *pkB;
int pkB_len;
uint8_t *b;
int b_len;
uint8_t *M1;
uint64_t M1_len;
const uint8_t *M2;
int M2_len;
uint8_t *v;
int v_len;
uint8_t *salt;
int salt_len;
};
struct pair_setup_context
{
struct pair_definition *type;
int setup_is_completed;
const char *errmsg;
// Hex-formatet concatenation of public + private, 0-terminated
char auth_key[2 * (crypto_sign_PUBLICKEYBYTES + crypto_sign_SECRETKEYBYTES) + 1];
union pair_setup_union
{
struct pair_client_setup_context client;
struct pair_server_setup_context server;
} sctx;
};
struct pair_client_verify_context
{
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];
};
struct pair_verify_context
{
struct pair_definition *type;
int verify_is_completed;
const char *errmsg;
union pair_verify_union
{
struct pair_client_verify_context client;
} vctx;
};
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
{
int (*pair_setup_new)(struct pair_setup_context *sctx, 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);
int (*pair_verify_new)(struct pair_verify_context *vctx, const char *hexkey, const char *device_id);
void (*pair_verify_free)(struct pair_verify_context *vctx);
int (*pair_verify_result)(const uint8_t **key, size_t *key_len, struct pair_verify_context *vctx);
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);
int (*pair_state_get)(const char **errmsg, const uint8_t *data, size_t data_len);
};
/* -------------------- 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)
#define bnum_mod(bn, a, b) gcry_mpi_mod(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);
}
__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m)
{
gcry_mpi_addm(bn, a, b, m);
}
#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_mod(bnum bn, bnum a, bnum b)
{
// No error handling
BN_CTX *ctx = BN_CTX_new();
BN_mod(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);
}
__attribute__((unused)) static void bnum_modadd(bnum bn, bnum a, bnum b, bnum m)
{
// No error handling
BN_CTX *ctx = BN_CTX_new();
BN_mod_add(bn, a, b, m, 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

218
src/pair_ap/pair-tlv.c Normal file
View File

@@ -0,0 +1,218 @@
/*
* TLV helpers are adapted from ESP homekit:
* <https://github.com/maximkulkin/esp-homekit>
*
* 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 <inttypes.h>
#include "pair-tlv.h"
#include "pair-internal.h"
static int
tlv_add_value_(pair_tlv_values_t *values, uint8_t type, uint8_t *value, size_t size) {
pair_tlv_t *tlv = malloc(sizeof(pair_tlv_t));
if (!tlv) {
return PAIR_TLV_ERROR_MEMORY;
}
tlv->type = type;
tlv->size = size;
tlv->value = value;
tlv->next = NULL;
if (!values->head) {
values->head = tlv;
} else {
pair_tlv_t *t = values->head;
while (t->next) {
t = t->next;
}
t->next = tlv;
}
return 0;
}
pair_tlv_values_t *
pair_tlv_new() {
pair_tlv_values_t *values = malloc(sizeof(pair_tlv_values_t));
if (!values)
return NULL;
values->head = NULL;
return values;
}
void
pair_tlv_free(pair_tlv_values_t *values) {
pair_tlv_t *t = values->head;
while (t) {
pair_tlv_t *t2 = t;
t = t->next;
if (t2->value)
free(t2->value);
free(t2);
}
free(values);
}
int
pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size) {
uint8_t *data = NULL;
int ret;
if (size) {
data = malloc(size);
if (!data) {
return PAIR_TLV_ERROR_MEMORY;
}
memcpy(data, value, size);
}
ret = tlv_add_value_(values, type, data, size);
if (ret < 0)
free(data);
return ret;
}
pair_tlv_t *
pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type) {
pair_tlv_t *t = values->head;
while (t) {
if (t->type == type)
return t;
t = t->next;
}
return NULL;
}
int
pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size) {
size_t required_size = 0;
pair_tlv_t *t = values->head;
while (t) {
required_size += t->size + 2 * ((t->size + 254) / 255);
t = t->next;
}
if (*size < required_size) {
*size = required_size;
return PAIR_TLV_ERROR_INSUFFICIENT_SIZE;
}
*size = required_size;
t = values->head;
while (t) {
uint8_t *data = t->value;
if (!t->size) {
buffer[0] = t->type;
buffer[1] = 0;
buffer += 2;
t = t->next;
continue;
}
size_t remaining = t->size;
while (remaining) {
buffer[0] = t->type;
size_t chunk_size = (remaining > 255) ? 255 : remaining;
buffer[1] = chunk_size;
memcpy(&buffer[2], data, chunk_size);
remaining -= chunk_size;
buffer += chunk_size + 2;
data += chunk_size;
}
t = t->next;
}
return 0;
}
int
pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values) {
size_t i = 0;
int ret;
while (i < length) {
uint8_t type = buffer[i];
size_t size = 0;
uint8_t *data = NULL;
// scan TLVs to accumulate total size of subsequent TLVs with same type (chunked data)
size_t j = i;
while (j < length && buffer[j] == type && buffer[j+1] == 255) {
size_t chunk_size = buffer[j+1];
size += chunk_size;
j += chunk_size + 2;
}
if (j < length && buffer[j] == type) {
size_t chunk_size = buffer[j+1];
size += chunk_size;
}
// allocate memory to hold all pieces of chunked data and copy data there
if (size != 0) {
data = malloc(size);
if (!data)
return PAIR_TLV_ERROR_MEMORY;
uint8_t *p = data;
size_t remaining = size;
while (remaining) {
size_t chunk_size = buffer[i+1];
memcpy(p, &buffer[i+2], chunk_size);
p += chunk_size;
i += chunk_size + 2;
remaining -= chunk_size;
}
}
ret = tlv_add_value_(values, type, data, size);
if (ret < 0) {
free(data);
return ret;
}
}
return 0;
}
#ifdef DEBUG_PAIR
void
pair_tlv_debug(const pair_tlv_values_t *values)
{
printf("Received TLV values\n");
for (pair_tlv_t *t=values->head; t; t=t->next)
{
printf("Type %d value (%zu bytes): \n", t->type, t->size);
hexdump("", t->value, t->size);
}
}
#endif

77
src/pair_ap/pair-tlv.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef __PAIR_AP_TLV_H__
#define __PAIR_AP_TLV_H__
#include <stdint.h>
#define PAIR_TLV_ERROR_MEMORY -1
#define PAIR_TLV_ERROR_INSUFFICIENT_SIZE -2
typedef enum {
TLVType_Method = 0, // (integer) Method to use for pairing. See PairMethod
TLVType_Identifier = 1, // (UTF-8) Identifier for authentication
TLVType_Salt = 2, // (bytes) 16+ bytes of random salt
TLVType_PublicKey = 3, // (bytes) Curve25519, SRP public key or signed Ed25519 key
TLVType_Proof = 4, // (bytes) Ed25519 or SRP proof
TLVType_EncryptedData = 5, // (bytes) Encrypted data with auth tag at end
TLVType_State = 6, // (integer) State of the pairing process. 1=M1, 2=M2, etc.
TLVType_Error = 7, // (integer) Error code. Must only be present if error code is
// not 0. See TLVError
TLVType_RetryDelay = 8, // (integer) Seconds to delay until retrying a setup code
TLVType_Certificate = 9, // (bytes) X.509 Certificate
TLVType_Signature = 10, // (bytes) Ed25519
TLVType_Permissions = 11, // (integer) Bit value describing permissions of the controller
// being added.
// None (0x00): Regular user
// Bit 1 (0x01): Admin that is able to add and remove
// pairings against the accessory
TLVType_FragmentData = 13, // (bytes) Non-last fragment of data. If length is 0,
// it's an ACK.
TLVType_FragmentLast = 14, // (bytes) Last fragment of data
TLVType_Flags = 19, // Added from airplay2_receiver
TLVType_Separator = 0xff,
} TLVType;
typedef enum {
TLVError_Unknown = 1, // Generic error to handle unexpected errors
TLVError_Authentication = 2, // Setup code or signature verification failed
TLVError_Backoff = 3, // Client must look at the retry delay TLV item and
// wait that many seconds before retrying
TLVError_MaxPeers = 4, // Server cannot accept any more pairings
TLVError_MaxTries = 5, // Server reached its maximum number of
// authentication attempts
TLVError_Unavailable = 6, // Server pairing method is unavailable
TLVError_Busy = 7, // Server is busy and cannot accept a pairing
// request at this time
} TLVError;
typedef struct _tlv {
struct _tlv *next;
uint8_t type;
uint8_t *value;
size_t size;
} pair_tlv_t;
typedef struct {
pair_tlv_t *head;
} pair_tlv_values_t;
pair_tlv_values_t *pair_tlv_new();
void pair_tlv_free(pair_tlv_values_t *values);
int pair_tlv_add_value(pair_tlv_values_t *values, uint8_t type, const uint8_t *value, size_t size);
pair_tlv_t *pair_tlv_get_value(const pair_tlv_values_t *values, uint8_t type);
int pair_tlv_format(const pair_tlv_values_t *values, uint8_t *buffer, size_t *size);
int pair_tlv_parse(const uint8_t *buffer, size_t length, pair_tlv_values_t *values);
#ifdef DEBUG_PAIR
void pair_tlv_debug(const pair_tlv_values_t *values);
#endif
#endif /* !__PAIR_AP_TLV_H__ */

623
src/pair_ap/pair.c Normal file
View File

@@ -0,0 +1,623 @@
/*
* 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_client_fruit;
extern struct pair_definition pair_client_homekit_normal;
extern struct pair_definition pair_client_homekit_transient;
extern struct pair_definition pair_server_homekit_transient;
// Must be in sync with enum pair_type
static struct pair_definition *pair[] = {
&pair_client_fruit,
&pair_client_homekit_normal,
&pair_client_homekit_transient,
&pair_server_homekit_transient,
};
/* -------------------------- 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)
{
struct pair_setup_context *sctx;
if (!pair[type]->pair_setup_new)
return NULL;
sctx = calloc(1, sizeof(struct pair_setup_context));
if (!sctx)
return NULL;
sctx->type = pair[type];
if (pair[type]->pair_setup_new(sctx, pin, device_id) < 0)
{
free(sctx);
return NULL;
}
return sctx;
}
void
pair_setup_free(struct pair_setup_context *sctx)
{
if (!sctx)
return;
if (sctx->type->pair_setup_free)
sctx->type->pair_setup_free(sctx);
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)
{
sctx->errmsg = "Setup request 1: Unsupported";
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)
{
sctx->errmsg = "Setup request 2: Unsupported";
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)
{
sctx->errmsg = "Setup request 3: Unsupported";
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)
{
sctx->errmsg = "Setup response 1: Unsupported";
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)
{
sctx->errmsg = "Setup response 2: Unsupported";
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)
{
sctx->errmsg = "Setup response 3: Unsupported";
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)
{
sctx->errmsg = "Setup result: Unsupported";
return -1;
}
if (sctx->type->pair_setup_result(&out_key, &out_len, sctx) != 0)
{
return -1;
}
if (2 * out_len + 1 > sizeof(sctx->auth_key))
{
sctx->errmsg = "Setup result: Invalid key length";
return -1;
}
ptr = sctx->auth_key;
for (i = 0; i < out_len; i++)
ptr += sprintf(ptr, "%02x", out_key[i]);
*ptr = '\0';
if (key)
*key = out_key;
if (key_len)
*key_len = out_len;
if (hexkey)
*hexkey = sctx->auth_key;
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;
if (!pair[type]->pair_verify_new)
return NULL;
vctx = calloc(1, sizeof(struct pair_verify_context));
if (!vctx)
return NULL;
vctx->type = pair[type];
if (pair[type]->pair_verify_new(vctx, hexkey, device_id) < 0)
{
free(vctx);
return NULL;
}
return vctx;
}
void
pair_verify_free(struct pair_verify_context *vctx)
{
if (!vctx)
return;
if (vctx->type->pair_verify_free)
vctx->type->pair_verify_free(vctx);
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)
{
vctx->errmsg = "Verify request 1: Unsupported";
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)
{
vctx->errmsg = "Verify request 2: Unsupported";
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)
{
vctx->errmsg = "Verify response 1: Unsupported";
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)
{
vctx->errmsg = "Verify response 2: Unsupported";
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;
}
if (!vctx->type->pair_verify_result)
return -1;
if (vctx->type->pair_verify_result(shared_secret, shared_secret_len, vctx) != 0)
return -1;
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)
{
cctx->errmsg = "Encryption unsupported";
return -1;
}
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)
{
cctx->errmsg = "Decryption unsupported";
return -1;
}
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;
}
int
pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len)
{
if (!pair[type]->pair_state_get)
{
*errmsg = "Getting pair state unsupported";
return -1;
}
return pair[type]->pair_state_get(errmsg, data, data_len);
}

145
src/pair_ap/pair.h Normal file
View File

@@ -0,0 +1,145 @@
#ifndef __PAIR_AP_H__
#define __PAIR_AP_H__
#include <stdint.h>
#define PAIR_AP_VERSION_MAJOR 0
#define PAIR_AP_VERSION_MINOR 2
enum pair_type
{
// This is the pairing type required for Apple TV device verification, which
// became mandatory with tvOS 10.2.
PAIR_CLIENT_FRUIT,
// This is the Homekit type required for AirPlay 2 with both PIN setup and
// verification
PAIR_CLIENT_HOMEKIT_NORMAL,
// Same as normal except PIN is fixed to 3939 and stops after setup step 2,
// when session key is established. This is the only mode where the server
// side is also supported.
PAIR_CLIENT_HOMEKIT_TRANSIENT,
PAIR_SERVER_HOMEKIT_TRANSIENT,
};
struct pair_setup_context;
struct pair_verify_context;
struct pair_cipher_context;
/* Client
* When you have the pin-code (must be 4 bytes), create a new context with this
* function and then call pair_setup_request1(). device_id is only
* required for homekit pairing, where it should have length 16.
*
* Server
* Create a new context with the pin-code to verify with, then when the request
* is received use pair_setup_response1() to read it, and then reply with using
* pair_setup_request1().
*/
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);
/* For parsing an incoming message to see what type ("state") it is. Mostly
* useful for servers. Returns 1-6 for pair-setup and 1-4 for pair-verify.
*/
int
pair_state_get(enum pair_type type, const char **errmsg, const uint8_t *data, size_t data_len);
#endif /* !__PAIR_AP_H__ */

1066
src/pair_ap/pair_fruit.c Normal file

File diff suppressed because it is too large Load Diff

2300
src/pair_ap/pair_homekit.c Normal file

File diff suppressed because it is too large Load Diff