Files
Mike Brady b405c0b896 Version 5.0 Major Release.
New Features:
Multi-Channel and High-Resolution Audio Support
48,000 frames per second ("48k") operation.
48k lossless stereo support.
5.1 and 7.1 surround sound support.
Multi-channel and multi-rate operation on ALSA, PipeWire, PulseAudio, FreeBSD, stdout and Unix pipe output backends.
Automatic Audio Format Selection
Flexible and controllable output format selection.
Automatic rate, sample format, and channel count selection.
Full FFmpeg Integration
Support for transcoding.
Advanced resampling capabilities.
New audio format support.
Enhanced Resampling
New vernier resampling and interpolation method optimized for low-power CPUs.
Better performance on resource-constrained devices.

Convolution and Loudness Enhancements:
Convolution system is now multithreaded and works on stereo and multichannel audio at 48k and 44.1k.
Multiple impulse response (IR) files can now be provided via convolution_ir_files setting.
New convolution_thread_pool_size setting for multithreaded processing (defaults to 1).
Loudness processing now works with stereo and multichannel audio at 48k and 44.1k.
Updated to the most recent HiFi-LoFi FFT convolver.

MQTT Enhancements:
Added new publish_retain boolean option. When enabled, published MQTT messages have the retain flag set, so the MQTT broker stores the last message per topic and new subscribers receive the most recent value immediately. Thanks to lululombard for PR #2142.

D-Bus Enhancements:
Added new dbus_default_message_bus command-line argument (can be system or session) to set the default message bus for both D-Bus native service and MPRIS service.

Performance Improvements:
Enhanced compatibility with AirPlay 2 AutoMix and Smart Tracklists resulting in less unexplained track skipping.
Better operation on low-power devices down to Raspberry Pi B.
Improved efficiency on embedded systems.
Enhanced timestamp handling for better synchronization.
Improved sync error calculation.
Rebuilt buffered audio processor for cleaner handling of immediate and deferred flush requests.

Docker Enhancements:
Reduced Docker image sizes with slimmed-down FFmpeg library.
Removed dhclient from Docker images for smaller footprint.

Bug Fixes:
Fixed MQTT warning on service startup: "Could not establish a mqtt connection". The startup script now correctly states that the mosquitto service is required. Thanks to Hugo Villeneuve for PR #2137.
Fixed compatibility with mbedtls library version 3.4+ (present on recent Linux versions). Thanks to Christian Beier for finding and fixing the bug.
Fixed PulseAudio backend so that PA_ERR_NODATA returns "No latency data yet". Thanks to Vladimir Shakov for the report and fix.
Ensured old flush requests are deleted when a new play session starts. Thanks to saujanyashah for the report.
Fixed format warnings on 64-bit and 32-bit systems
Removed compilation warnings on 32-bit builds
Improved argument checking for debug(), inform(), warn() and die() functions
Fixed "daemon" typos throughout codebase. Thanks to Chris Boot for PR #1981.
Added warning if a convolution impulse response file cannot be read due to bad path or permissions

Build System Improvements:
Unified service file with variable substitution for Avahi support, making it easier to add future service dependencies. Thanks to Hugo Villeneuve.
Network interface selection now only considers interfaces that are up, running and not loopback interfaces. Thanks to Carl Johnson for the suggestion.
Configuration File Changes and Deprecations

New settings: convolution_ir_files (replaces convolution_ir_file), convolution_enabled (replaces convolution), convolution_max_length_in_seconds (replaces convolution_max_length), loudness_enabled (replaces loudness).
New convolution_thread_pool_size setting (defaults to 1).
Deprecated settings: convolution_ir_file, convolution, convolution_max_length, loudness.
Corresponding D-Bus methods and properties have been updated.

Deprecation Notice:
The Jack Audio and soundio backends are deprecated and will be removed in a future release. Consider using the updated PipeWire backend instead.

Documentation Updates
Updated BUILD.md with latest build instructions.
Updated AIRPLAY2.md with feature information.
Enhanced convolution and loudness documentation.

Maintenance:
Fixed FFmpeg deprecation warnings.
Bumped actions/checkout from 6.0.1 to 6.0.2.
Bumped docker/login-action from 3.6.0 to 3.7.0.
Bumped docker/build-push-action from 6.13.0 to 6.15.0.
Bumped docker/setup-qemu-action from 3.4.0 to 3.6.0.
Bumped docker/setup-buildx-action from 3.9.0 to 3.10.0.
2026-02-13 15:17:40 +00:00

267 lines
11 KiB
C

#ifndef __PAIR_AP_H__
#define __PAIR_AP_H__
#include <stdint.h>
#define PAIR_AP_VERSION_MAJOR 0
#define PAIR_AP_VERSION_MINOR 14
#define PAIR_AP_DEVICE_ID_LEN_MAX 64
#define PAIR_AP_POST_PIN_START "POST /pair-pin-start"
#define PAIR_AP_POST_SETUP "POST /pair-setup"
#define PAIR_AP_POST_VERIFY "POST /pair-verify"
#define PAIR_AP_POST_ADD "POST /pair-add"
#define PAIR_AP_POST_LIST "POST /pair-list"
#define PAIR_AP_POST_REMOVE "POST /pair-remove"
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
PAIR_CLIENT_HOMEKIT_TRANSIENT,
// Server side implementation supporting both transient and normal mode,
// letting client choose mode. If a PIN is with pair_setup_new() then only
// normal mode will be possible.
PAIR_SERVER_HOMEKIT,
};
/* This struct stores the various forms of pairing results. The shared secret
* is used to initialise an encrypted session via pair_cipher_new(). For
* non-transient client pair setup, you also get a key string (client_setup_keys) from
* pair_setup_result() that you can store and use to later initialise
* pair_verify_new(). For non-transient server pair setup, you can either:
* - Register an "add pairing" callback (add_cb) with pair_setup_new(), and
* then save the client id and key in the callback (see server-example.c for
* this approach).
* - Check pairing result with pair_setup_result() and if successful read and
* store the client id and key from the result struct.
* - Decide not to authenticate clients during pair-verify (set get_cb to NULL)
* in which case you don't need to save client ids and keys from pair-setup.
*
* Table showing returned data (everything else will be zeroed):
*
* | pair-setup | pair-verify
* --------------------------------|-------------------------------|--------------
* PAIR_CLIENT_FRUIT | client keys | shared secret
* PAIR_CLIENT_HOMEKIT_NORMAL | client keys, server public | shared secret
| key, server id | shared secret
* PAIR_CLIENT_HOMEKIT_TRANSIENT | shared secret | n/a
* PAIR_SERVER_HOMEKIT (normal) | client public key, client id | shared secret
* PAIR_SERVER_HOMEKIT (transient) | shared secret | n/a
*/
struct pair_result
{
char device_id[PAIR_AP_DEVICE_ID_LEN_MAX]; // ID of the peer
uint8_t client_private_key[64];
uint8_t client_public_key[32];
uint8_t server_public_key[32];
uint8_t shared_secret[64];
size_t shared_secret_len; // Will be 32 (normal) or 64 (transient)
};
struct pair_setup_context;
struct pair_verify_context;
struct pair_cipher_context;
typedef int (*pair_cb)(uint8_t public_key[32], const char *device_id, void *cb_arg);
typedef void (*pair_list_cb)(pair_cb list_cb, void *list_cb_arg, void *cb_arg);
/* ------------------------------- pair setup ------------------------------- */
/* Client
* When you have the pin-code (must be 4 chars), create a new context with this
* function and then call pair_setup() or pair_setup_request1(). device_id is
* only required for Homekit pairing. If the client previously paired
* (non-transient) and has saved credentials, it should instead skip setup and
* only do verification. The callback is only for Homekit, and you can leave it
* at NULL if you don't care about saving ID and key of the server for later
* verification (then you also set get_cb to NULL in pair_verify_new), or if you
* will read the id and key via pair_setup_result.
*
* Server
* The client will make a connection and then at some point make a /pair-setup
* or a /pair-verify. The server should:
* - new /pair-setup: create a setup context with a pin-code (or NULL to allow
* transient pairing), and then call pair_setup() to process request and
* construct reply (also for subsequent /pair-setup requests)
* - new /pair_verify: create a verify context and then call pair_verify()
* to process request and construct reply (also for subsequent /pair-verify
* requests)
*/
struct pair_setup_context *
pair_setup_new(enum pair_type type, const char *pin, pair_cb add_cb, void *cb_arg, 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);
/* Will create a request (if client) or response (if server) based on the setup
* context and last message from the peer. If this is the first client request
* then set *in to NULL. Returns negative on error.
*/
int
pair_setup(uint8_t **out, size_t *out_len, struct pair_setup_context *sctx, const uint8_t *in, size_t in_len);
/* Returns the result of a pairing, or negative if pairing is not completed. See
* 'struct pair_result' for info about pairing results. The string is a
* representation of the result that is easy to persist and can be used to feed
* back into pair_verify_new. The result and string becomes invalid when you
* free sctx.
*/
int
pair_setup_result(const char **client_setup_keys, struct pair_result **result, struct pair_setup_context *sctx);
/* These are for constructing specific message types and reading specific
* message types. Not needed for Homekit pairing if you use pair_setup().
*/
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 *in, size_t in_len);
int
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len);
int
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *in, size_t in_len);
/* ------------------------------ pair verify ------------------------------- */
/* Client
* When you have completed pair setup you get a string containing some keys
* from pair_setup_result(). Give the string as input to this function to create
* a verification context. Set the callback to NULL. Then call pair_verify().
* The device_id is required for Homekit pairing.
*
* Server
* When you get a pair verify request from a new peer, create a new context with
* client_setup_keys set to NULL, with a callback set and the server's device ID
* (same as for setup). Then call pair_verify(). The callback is used to get
* the persisted client public key (saved after pair setup), so the client can
* be verified. You can set the callback to NULL if you don't care about that.
* If set, the callback is made as part of pair_verify_response2. The job of the
* callback is to fill out the public_key with the public key from the setup
* stage (see 'struct pair_result'). If the client device id is not known (i.e.
* it has not completed pair-setup), return -1.
*/
struct pair_verify_context *
pair_verify_new(enum pair_type type, const char *client_setup_keys, pair_cb get_cb, void *cb_arg, 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);
/* Will create a request (if client) or response (if server) based on the verify
* context and last message from the peer. If this is the first client request
* then set *in to NULL. Returns negative on error.
*/
int
pair_verify(uint8_t **out, size_t *out_len, struct pair_verify_context *sctx, const uint8_t *in, size_t in_len);
/* Returns a pointer to the result of the pairing. Only the shared secret will
* be filled out. Note that the result become invalid when you free vctx.
*/
int
pair_verify_result(struct pair_result **result, struct pair_verify_context *vctx);
/* These are for constructing specific message types and reading specific
* message types. Not needed for Homekit pairing where you can use pair_verify().
*/
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 *in, size_t in_len);
int
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *in, size_t in_len);
/* ------------------------------- ciphering -------------------------------- */
/* When you have completed the verification you can extract a shared secret with
* pair_verify_result() - or, in case of transient pairing, from
* pair_setup_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, const char *dynamic_salt_suffix);
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, const 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, const 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);
/* --------------------------------- other ---------------------------------- */
/* These are for Homekit pairing where they are called by the controller, e.g.
* the Home app
*
* TODO this part is currenly not working
*/
int
pair_add(enum pair_type type, uint8_t **out, size_t *out_len, pair_cb add_cb, void *cb_arg, const uint8_t *in, size_t in_len);
int
pair_remove(enum pair_type type, uint8_t **out, size_t *out_len, pair_cb remove_cb, void *cb_arg, const uint8_t *in, size_t in_len);
int
pair_list(enum pair_type type, uint8_t **out, size_t *out_len, pair_list_cb list_cb, void *cb_arg, const uint8_t *in, size_t in_len);
/* 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 *in, size_t in_len);
/* For servers, pair_ap calculates the public key using device_id as a seed.
* This function returns that public key.
*/
void
pair_public_key_get(enum pair_type type, uint8_t server_public_key[32], const char *device_id);
#endif /* !__PAIR_AP_H__ */