module-raop/rtsp-client: Add cipher support

This commit is contained in:
Christian Glombek 2023-08-01 21:07:19 +02:00
parent b4b3a53384
commit 77437f5a79
3 changed files with 109 additions and 28 deletions

View file

@ -269,8 +269,7 @@ struct impl {
uint64_t salt_len;
uint8_t shared_secret[64];
size_t shared_secret_len; // Will be 32 (normal) or 64 (transient)
//struct pw_rtsp_cipher_context *control_cipher_ctx;
struct pw_rtsp_cipher_context *cipher_ctx;
uint16_t control_port;
int control_fd;
@ -1019,7 +1018,7 @@ static int rtsp_setup_reply(void *data, int status, const struct spa_dict *heade
int res;
pw_log_info("setup status: %d", status);
// TODO
if ((str = spa_dict_lookup(headers, "Session")) == NULL) {
pw_log_error("missing Session header");
return 0;
@ -1183,13 +1182,13 @@ hkdf_extract_expand(uint8_t *okm, size_t okm_len, const uint8_t *ikm, size_t ikm
EVP_PKEY_CTX_free(pctx);
return -1;
}
/*
static pw_rtsp_cipher_context *ap_cipher_context_new(const uint8_t *shared_secret, size_t shared_secret_len, const char *enc_salt, const char *enc_info, const char *dec_salt, const char *dec_info)
static struct pw_rtsp_cipher_context *ap_cipher_context_new(const uint8_t *shared_secret, size_t shared_secret_len, const char *enc_salt, const char *enc_info, const char *dec_salt, const char *dec_info)
{
struct pw_rtsp_cipher_context *cctx;
int ret;
cctx = calloc(1, sizeof(struct pw_rtsp_cipher_context));
cctx = calloc(1, sizeof(*cctx));
if (!cctx)
goto error;
ret = hkdf_extract_expand(cctx->encryption_key, sizeof(cctx->encryption_key), shared_secret, shared_secret_len, enc_salt, enc_info);
@ -1361,7 +1360,7 @@ static ssize_t ap_decrypt(uint8_t **plaintext, size_t *plaintext_len,
return cipher_block - ciphertext;
}
*/
static int rtsp_do_setup(struct impl *impl)
{
int res;
@ -1387,7 +1386,7 @@ static int rtsp_do_setup(struct impl *impl)
impl->timing_source = pw_loop_add_io(impl->loop, impl->timing_fd,
SPA_IO_IN, false, on_timing_source_io, impl);
switch (impl->protocol) {
switch (impl->encryption) {
case CRYPTO_PAIR_TRANSIENT:
// Encryption/decryption of control channel
const char *control_salt = AP_CONTROL_SALT;
@ -1400,7 +1399,7 @@ static int rtsp_do_setup(struct impl *impl)
wplist_dict_add_uint(stream, "controlPort", impl->control_port);
wplist_dict_add_uint(stream, "ct", impl->codec); // Compression type, 1 LPCM, 2 ALAC, 3 AAC, 4 AAC ELD, 32 OPUS
wplist_dict_add_bool(stream, "isMedia", true); // ?
//wplist_dict_add_uint(stream, "latencyMax", 88200); // TODO how do these latencys work?
wplist_dict_add_uint(stream, "latencyMax", 88200); // ?
wplist_dict_add_uint(stream, "latencyMin", RAOP_LATENCY_MIN);
wplist_dict_add_data(stream, "shk", impl->shared_secret, impl->shared_secret_len);
wplist_dict_add_uint(stream, "spf", FRAMES_PER_UDP_PACKET);
@ -1418,12 +1417,16 @@ static int rtsp_do_setup(struct impl *impl)
const char *url = pw_rtsp_client_get_url(impl->rtsp);
//impl->control_cipher_ctx = ap_cipher_context_new(impl->shared_secret, impl->shared_secret_len, control_salt, enc_info, control_salt, dec_info);
//if (!impl->control_cipher_ctx) {
// pw_log_error("Could not create control cipher");
// goto error;
//}
//struct pw_rtsp_cipher *cipher = ap_cipher_new(ap_decrypt, ap_encrypt);
impl->cipher_ctx = ap_cipher_context_new(impl->shared_secret, impl->shared_secret_len, control_salt, enc_info, control_salt, dec_info);
if (!impl->cipher_ctx) {
pw_log_error("Could not create control cipher");
goto error;
}
pw_rtsp_client_set_cipher_ctx(impl->rtsp, impl->cipher_ctx);
pw_rtsp_client_set_encryption(impl->rtsp, ap_encrypt);
pw_rtsp_client_set_decryption(impl->rtsp, ap_decrypt);
res = pw_rtsp_client_url_send(impl->rtsp, url, "SETUP", &impl->headers->dict,
"application/x-apple-binary-plist", content, content_len,
@ -1855,8 +1858,8 @@ static int rtsp_ap2_pair_setup2_reply(void *data, int status, const struct spa_d
// TODO
pw_properties_set(impl->headers, "X-Apple-HKP", NULL);
//return rtsp_do_setup(impl);
return 0;
return rtsp_do_setup(impl);
//return 0;
error:
tlv_free(response);

View file

@ -56,6 +56,9 @@ struct pw_rtsp_client {
unsigned int need_flush:1;
enum client_recv_state recv_state;
struct pw_rtsp_cipher_context *cipher_ctx;
ssize_t (*decrypt) (uint8_t **plaintext, size_t *plaintext_len, const uint8_t *ciphertext, size_t ciphertext_len, struct pw_rtsp_cipher_context *cctx);
ssize_t (*encrypt) (uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pw_rtsp_cipher_context *cctx);
int status;
char line_buf[1024];
size_t line_pos;
@ -92,12 +95,33 @@ struct pw_rtsp_client *pw_rtsp_client_new(struct pw_loop *main_loop,
client->headers = pw_properties_new(NULL, NULL);
pw_array_init(&client->content, 4096);
client->recv_state = CLIENT_RECV_NONE;
client->cipher_ctx = NULL;
client->decrypt = NULL;
client->encrypt = NULL;
pw_log_info("new client %p", client);
return client;
}
void pw_rtsp_client_set_decryption(struct pw_rtsp_client *client,
ssize_t (*decrypt) (uint8_t **plaintext, size_t *plaintext_len, const uint8_t *ciphertext, size_t ciphertext_len, struct pw_rtsp_cipher_context *cctx))
{
client->decrypt = decrypt;
}
void pw_rtsp_client_set_encryption(struct pw_rtsp_client *client,
ssize_t (*encrypt) (uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pw_rtsp_cipher_context *cctx))
{
client->encrypt = encrypt;
}
void pw_rtsp_client_set_cipher_ctx(struct pw_rtsp_client *client,
struct pw_rtsp_cipher_context *cipher_ctx)
{
client->cipher_ctx = cipher_ctx;
}
void pw_rtsp_client_destroy(struct pw_rtsp_client *client)
{
pw_log_info("destroy client %p", client);
@ -154,6 +178,8 @@ int pw_rtsp_client_get_local_ip(struct pw_rtsp_client *client,
static int handle_connect(struct pw_rtsp_client *client, int fd)
{
pw_log_info("enter handle connect");
int res, ip_version;
socklen_t len;
char local_ip[INET6_ADDRSTRLEN];
@ -185,9 +211,12 @@ static int handle_connect(struct pw_rtsp_client *client, int fd)
client->recv_state = CLIENT_RECV_STATUS;
pw_properties_clear(client->headers);
pw_array_reset(&client->content);
client->status = 0;
client->line_pos = 0;
client->content_length = 0;
client->decrypt = NULL;
client->encrypt = NULL;
pw_rtsp_client_emit_connected(client);
@ -196,7 +225,7 @@ static int handle_connect(struct pw_rtsp_client *client, int fd)
static int read_line(struct pw_rtsp_client *client, char **buf)
{
int res;
int res, idx = 0;
while (true) {
uint8_t c;
@ -217,15 +246,17 @@ static int read_line(struct pw_rtsp_client *client, char **buf)
client->line_pos = 0;
if (buf)
*buf = client->line_buf;
return 1;
return idx;
}
if (c == '\r')
continue;
if (client->line_pos < sizeof(client->line_buf) - 1)
if (client->line_pos < sizeof(client->line_buf) - 1) {
client->line_buf[client->line_pos++] = c;
idx++;
}
client->line_buf[client->line_pos] = '\0';
}
return 0;
return idx;
}
static struct message *find_pending(struct pw_rtsp_client *client, uint32_t cseq)
@ -355,7 +386,7 @@ static int process_content(struct pw_rtsp_client *client)
spa_assert((size_t) res <= client->content_length);
client->content_length -= res;
pw_log_info("processing content (%ld bytes):\n%s", client->content.size, client->content.data);
pw_log_info("processing content (%ld bytes):\n%s", client->content.size, (char *)client->content.data);
}
if (client->content_length == 0)
@ -370,11 +401,24 @@ static int process_input(struct pw_rtsp_client *client)
char *buf = NULL;
int res;
if ((res = read_line(client, &buf)) <= 0)
if ((res = read_line(client, &buf)) < 0) {
pw_log_debug("ret 1: %d", res);
return res;
pw_log_debug("received line: %s", buf);
}
pw_log_debug("received line (%ld bytes): %s", res, buf);
if (res > 0 && client->decrypt != NULL && client->cipher_ctx != NULL) {
pw_log_debug("decrypting");
uint8_t *dec_buf = malloc(1024);
size_t dec_len;
ssize_t dec;
dec = client->decrypt(dec_buf, &dec_len, (const uint8_t *)buf, res, client->cipher_ctx);
if (dec < 0) {
pw_log_debug("ret 2");
return dec;
}
buf = dec_buf;
pw_log_debug("plain line: %s", buf);
}
switch (client->recv_state) {
case CLIENT_RECV_STATUS:
return process_status(client, buf);
@ -446,14 +490,17 @@ on_source_io(void *data, int fd, uint32_t mask)
int res;
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
pw_log_info("ERR");
res = -EPIPE;
goto error;
}
if (mask & SPA_IO_IN) {
pw_log_info("IN");
if ((res = process_input(client)) < 0)
goto error;
}
if (mask & SPA_IO_OUT || client->need_flush) {
pw_log_info("OUT");
if (client->connecting) {
if ((res = handle_connect(client, fd)) < 0)
goto error;
@ -553,6 +600,9 @@ int pw_rtsp_client_disconnect(struct pw_rtsp_client *client)
client->url = NULL;
free(client->session_id);
client->session_id = NULL;
if (client->cipher_ctx != NULL)
free(client->cipher_ctx);
client->cipher_ctx = NULL;
spa_list_consume(msg, &client->messages, link) {
spa_list_remove(&msg->link);
@ -599,8 +649,19 @@ int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url,
fclose(f);
msg->data = SPA_PTROFF(msg, sizeof(*msg), void);
msg->len = len - sizeof(*msg);
if (client->encrypt != NULL && client->cipher_ctx != NULL) {
uint8_t *enc_buf;
size_t enc_len;
ssize_t enc = client->encrypt(&enc_buf, &enc_len, SPA_PTROFF(msg, sizeof(*msg), void), len - sizeof(*msg), client->cipher_ctx);
if (enc < 0)
return enc;
msg->data = enc_buf;
msg->len = enc_len;
}
else {
msg->data = SPA_PTROFF(msg, sizeof(*msg), void);
msg->len = len - sizeof(*msg);
}
msg->offset = 0;
msg->reply = reply;
msg->user_data = user_data;

View file

@ -30,6 +30,15 @@ struct pw_rtsp_client_events {
};
struct pw_rtsp_cipher_context {
uint8_t encryption_key[32];
uint8_t decryption_key[32];
uint64_t encryption_counter;
uint64_t decryption_counter;
uint64_t encryption_counter_prev;
uint64_t decryption_counter_prev;
};
struct pw_rtsp_client * pw_rtsp_client_new(struct pw_loop *main_loop,
struct pw_properties *props,
size_t user_data_size);
@ -64,6 +73,14 @@ int pw_rtsp_client_send(struct pw_rtsp_client *client,
int (*reply) (void *user_data, int status, const struct spa_dict *headers, const struct pw_array *content),
void *user_data);
void pw_rtsp_client_set_decryption(struct pw_rtsp_client *client,
ssize_t (*decrypt) (uint8_t **plaintext, size_t *plaintext_len, const uint8_t *ciphertext, size_t ciphertext_len, struct pw_rtsp_cipher_context *cctx));
void pw_rtsp_client_set_encryption(struct pw_rtsp_client *client,
ssize_t (*encrypt) (uint8_t **ciphertext, size_t *ciphertext_len, const uint8_t *plaintext, size_t plaintext_len, struct pw_rtsp_cipher_context *cctx));
void pw_rtsp_client_set_cipher_ctx(struct pw_rtsp_client *client,
struct pw_rtsp_cipher_context *cipher_ctx);
#ifdef __cplusplus
}