Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Homekit transient pairing #336

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,7 @@ if ( NOT APPLE )
target_include_directories( airplay PRIVATE ${DNSSD_INCLUDE_DIR} )
endif()
endif()

pkg_check_modules(SODIUM REQUIRED libsodium)
include_directories(${SODIUM_INCLUDE_DIRS})
target_link_libraries(airplay ${SODIUM_LIBRARIES})
183 changes: 183 additions & 0 deletions lib/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/pem.h>
#include <openssl/kdf.h>

#include <assert.h>
#include <stdlib.h>
Expand Down Expand Up @@ -580,3 +581,185 @@ void pk_to_base64(const unsigned char *pk, int pk_len, char *pk_base64, int len)
memcpy(pk_base64,(*bufferPtr).data, len64);
}

// CHACHA

/* Executes SHA512 RFC 5869 extract + expand, writing a derived key to okm

hkdfExtract(SHA512, salt, salt_len, ikm, ikm_len, prk);
hkdfExpand(SHA512, prk, SHA512_LEN, info, info_len, okm, okm_len);
*/
static int
hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm_len, uint8_t * salt, uint8_t * info)
{
EVP_PKEY_CTX *pctx;

if (okm_len > SHA512_DIGEST_LENGTH)
return -1;
if (! (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)))
return -1;
if (EVP_PKEY_derive_init(pctx) <= 0)
goto error;
if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha512()) <= 0)
goto error;
if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)salt, strlen((const char *)salt)) <= 0)
goto error;
if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len) <= 0)
goto error;
if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (const unsigned char *)info, strlen((const char *)info)) <= 0)
goto error;
if (EVP_PKEY_derive(pctx, okm, &okm_len) <= 0)
goto error;

EVP_PKEY_CTX_free(pctx);
return 0;

error:
EVP_PKEY_CTX_free(pctx);
return -1;
}

/* Creates the decryption key or the encryption key (or both) for HomeKit-based connections
chacha_setup_keys(ctx, "DataStream-Salt123123123", "DataStream-Output-Encryption-Key", NULL, NULL);
chacha_setup_keys(ctx, "Events-Salt", "Events-Write-Encryption-Key", "Events-Salt", "Events-Read-Encryption-Key")

Returns:
0 on success
-1 on decryption key fail
-2 on encryption key fail
*/
int
chacha_setup_keys(chacha_ctx_t *ctx,
unsigned char *session_key,
unsigned char *decryption_salt, unsigned char *decryption_info,
unsigned char *encryption_salt, unsigned char *encryption_info)
{
assert(ctx);

ctx->decryption_counter = 0;
ctx->encryption_counter = 0;

if (decryption_salt != NULL) {
if (hkdf_extract_expand(ctx->decryption_key, CHACHA_KEY_LEN, session_key, HOMEKIT_SESSION_KEY_LEN, decryption_salt, decryption_info)) {
return -1;
}
}

if (encryption_salt != NULL) {
if (hkdf_extract_expand(ctx->encryption_key, CHACHA_KEY_LEN, session_key, HOMEKIT_SESSION_KEY_LEN, encryption_salt, encryption_info)) {
return -2;
}
}

return 0;
}

#define CHACHA_DEBUG 0

int
decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, chacha_ctx_t *chacha_ctx, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len)
{
EVP_CIPHER_CTX *ctx;
int len;
uint8_t nonce[12] = {0};
memcpy(nonce + 4, &chacha_ctx->decryption_counter, 8);
chacha_ctx->decryption_counter++;

#if CHACHA_DEBUG
const unsigned char * aad = ad;

printf("DECRYPTION INFO:");

printf("\n\nCipher: (%d)\n", cipher_len);
for (int i = 0; i < cipher_len; i++) {
printf("%02X ", cipher[i]);
}

printf("\n\nDecryption key (%d)\n", 32);
for (int i = 0; i < 32; i++) {
printf("%02X ", chacha_ctx->decryption_key[i]);
}

printf("\n\nAD (%d)\n", ad_len);
for (int i = 0; i < ad_len; i++) {
printf("%02X ", aad[i]);
}

printf("\n\nTag (%d)\n", tag_len);
for (int i = 0; i < tag_len; i++) {
printf("%02X ", tag[i]);
}

printf("\n\nNonce\n");
for (int i = 0; i < NONCE_LENGTH; i++) {
printf("%02X ", nonce[i]);
}
#endif

if (! (ctx = EVP_CIPHER_CTX_new()))
return -1;

if (EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, chacha_ctx->decryption_key, nonce) != 1)
goto error;

if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary
goto error;

if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag) != 1)
goto error;

if (ad_len > 0 && EVP_DecryptUpdate(ctx, NULL, &len, ad, ad_len) != 1)
goto error;

if (EVP_DecryptUpdate(ctx, plain, &len, cipher, cipher_len) != 1)
goto error;

if (EVP_DecryptFinal_ex(ctx, NULL, &len) != 1)
goto error;

EVP_CIPHER_CTX_free(ctx);
return 0;

error:
EVP_CIPHER_CTX_free(ctx);
return -1;
}

int
encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, chacha_ctx_t *chacha_ctx, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len)
{
EVP_CIPHER_CTX *ctx;
int len;
uint8_t nonce[12];
memcpy(nonce + 4, &chacha_ctx->encryption_counter, 8);
chacha_ctx->encryption_counter++;

if (! (ctx = EVP_CIPHER_CTX_new()))
return -1;

if (EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, chacha_ctx->encryption_key, nonce) != 1)
goto error;

if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) // Maybe not necessary
goto error;

if (ad_len > 0 && EVP_EncryptUpdate(ctx, NULL, &len, ad, ad_len) != 1)
goto error;

if (EVP_EncryptUpdate(ctx, cipher, &len, plain, plain_len) != 1)
goto error;

assert(len == plain_len);

if (EVP_EncryptFinal_ex(ctx, NULL, &len) != 1)
goto error;

if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, tag) != 1)
goto error;

EVP_CIPHER_CTX_free(ctx);
return 0;

error:
EVP_CIPHER_CTX_free(ctx);
return -1;
}
21 changes: 21 additions & 0 deletions lib/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,27 @@ void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len);
void sha_reset(sha_ctx_t *ctx);
void sha_destroy(sha_ctx_t *ctx);

// CHACHA

#define NONCE_LENGTH 12
#define CHACHA_KEY_LEN 32
#define HOMEKIT_SESSION_KEY_LEN 64

struct chacha_ctx_s {
unsigned char decryption_key[CHACHA_KEY_LEN];
uint64_t decryption_counter;
unsigned char encryption_key[CHACHA_KEY_LEN];
uint64_t encryption_counter;
};

typedef struct chacha_ctx_s chacha_ctx_t;

int chacha_setup_keys(chacha_ctx_t *ctx, unsigned char *session_key, unsigned char *decryption_salt, unsigned char *decryption_info, unsigned char *encryption_salt, unsigned char *encryption_info);

int decrypt_chacha(uint8_t *plain, const uint8_t *cipher, uint16_t cipher_len, chacha_ctx_t *chacha_ctx, const void *ad, uint8_t ad_len, uint8_t *tag, uint8_t tag_len);

int encrypt_chacha(uint8_t *cipher, const uint8_t *plain, size_t plain_len, chacha_ctx_t *chacha_ctx, const void *ad, size_t ad_len, uint8_t *tag, size_t tag_len);

#ifdef __cplusplus
}
#endif
Expand Down
7 changes: 7 additions & 0 deletions lib/dnssd.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,13 @@ dnssd_get_airplay_txt(dnssd_t *dnssd, int *length)
return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record);
}

const char *
dnssd_get_raop_txt(dnssd_t *dnssd, int *length)
{
*length = dnssd->TXTRecordGetLength(&dnssd->raop_record);
return dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record);
}

const char *
dnssd_get_name(dnssd_t *dnssd, int *length)
{
Expand Down
28 changes: 28 additions & 0 deletions lib/http_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include "http_request.h"
#include "llhttp/llhttp.h"
#include "crypto.h"

struct http_request_s {
llhttp_t parser;
Expand All @@ -39,6 +40,8 @@ struct http_request_s {
int datalen;

int complete;

chacha_ctx_t *chacha_ctx;
};

static int
Expand Down Expand Up @@ -309,3 +312,28 @@ http_request_get_header_string(http_request_t *request, char **header_str)
assert(p == &(str[len]));
return len;
}

void
http_request_begin_encryption(http_request_t *request, chacha_ctx_t *chacha_ctx) {
assert(request);

request->chacha_ctx = chacha_ctx;
}

int
http_request_get_encryption(http_request_t *request, chacha_ctx_t **chacha_ctx) {
assert(request);

if (request->chacha_ctx) {
*chacha_ctx = request->chacha_ctx;
return 1;
}

return 0;
}

// int http_request_decrypt(http_request_t *request, char* decryption_key, ) {
// assert(request);


// }
3 changes: 3 additions & 0 deletions lib/http_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

typedef struct http_request_s http_request_t;

typedef struct chacha_ctx_s chacha_ctx_t;

http_request_t *http_request_init(void);

Expand All @@ -32,6 +33,8 @@ const char *http_request_get_protocol(http_request_t *request);
const char *http_request_get_header(http_request_t *request, const char *name);
const char *http_request_get_data(http_request_t *request, int *datalen);
int http_request_get_header_string(http_request_t *request, char **header_str);
void http_request_begin_encryption(http_request_t *request, chacha_ctx_t *ctx);
int http_request_get_encryption(http_request_t *request, chacha_ctx_t **ctx);

void http_request_destroy(http_request_t *request);

Expand Down
Loading