Merge branch 'airplay2_5'

This commit is contained in:
ejurgensen 2021-01-10 15:06:10 +01:00
commit 541e022a0e
26 changed files with 7805 additions and 746 deletions

View File

@ -43,8 +43,6 @@ Optional packages:
--------------------|--------------------------|------------------------------------------------
Chromecast | `--enable-chromecast` | libgnutls*-dev libprotobuf-c-dev
Spotify | `--enable-spotify` | libspotify-dev
iTunes XML | `--disable-itunes` | libplist-dev
Device verification | `--disable-verification` | libplist-dev libsodium-dev
Player web UI | `--disable-webinterface` | libwebsockets-dev
Live web UI | `--with-libwebsockets` | libwebsockets-dev
Pulseaudio | `--with-pulseaudio` | libpulse-dev
@ -146,7 +144,8 @@ Afterwards, you can optionally install Oracle's newer version, and then
```bash
sudo port install \
autoconf automake libtool pkgconfig git gperf libgcrypt \
libunistring libconfuse ffmpeg libevent json-c libwebsockets curl
libunistring libconfuse ffmpeg libevent json-c libwebsockets curl \
libplist libsodium
```
Download, configure, build and install the Mini-XML library:
@ -170,8 +169,6 @@ Optional features require the following additional ports:
Feature | Configure argument | Ports
--------------------|--------------------------|-------------------
Chromecast | `--enable-chromecast` | gnutls protobuf-c
iTunes XML | `--disable-itunes` | libplist
Device verification | `--disable-verification` | libplist libsodium
Pulseaudio | `--with-pulseaudio` | pulseaudio
Clone the forked-daapd repo:
@ -263,21 +260,21 @@ Libraries:
from <https://github.com/json-c/json-c/wiki>
- libcurl
from <http://curl.haxx.se/libcurl/>
- libasound (optional - ALSA local audio)
often already installed as part of your distro
- libpulse (optional - Pulseaudio local audio)
from <https://www.freedesktop.org/wiki/Software/PulseAudio/Download/>
- libplist 0.16+ (optional - iTunes XML support and Apple TV device verification)
- libplist 0.16+
from <http://github.com/JonathanBeck/libplist/downloads>
- libsodium (optional - Apple TV device verification)
- libsodium
from <https://download.libsodium.org/doc/>
- libspotify (optional - Spotify support)
- libasound (optional - ALSA local audio)
often already installed as part of your distro
- libpulse (optional - Pulseaudio local audio)
from <https://www.freedesktop.org/wiki/Software/PulseAudio/Download/>
- libspotify (optional - Spotify support)
from <https://developer.spotify.com>
- libgnutls (optional - Chromecast support)
- libgnutls (optional - Chromecast support)
from <http://www.gnutls.org/>
- libprotobuf-c (optional - Chromecast support)
- libprotobuf-c (optional - Chromecast support)
from <https://github.com/protobuf-c/protobuf-c/wiki>
- libwebsockets 2.0.2+ (optional - websocket support)
- libwebsockets 2.0.2+ (optional - websocket support)
from <https://libwebsockets.org/>
If using binary packages, remember that you need the development packages to
@ -325,15 +322,9 @@ to run on systems without libspotify (the Spotify features will then be disabled
Support for LastFM scrobbling is optional. Use `--enable-lastfm` to enable this
feature.
Support for iTunes Music Library XML format is optional. Use `--disable-itunes`
to disable this feature.
Support for the MPD protocol is optional. Use `--disable-mpd` to disable this
feature.
Support for Apple TV device verification is optional. Use `--disable-verification`
to disable this feature.
Support for Chromecast devices is optional. Use `--enable-chromecast` to enable
this feature.

View File

@ -89,6 +89,10 @@ AC_SEARCH_LIBS([pthread_setname_np], [pthread],
[AC_SEARCH_LIBS([pthread_set_name_np], [pthread],
[AC_CHECK_FUNCS([pthread_set_name_np])])])
AC_SEARCH_LIBS([uuid_generate_random], [uuid],
[AC_DEFINE([HAVE_UUID], 1,
[Define to 1 if you have uuid_generate_random function])])
AC_SEARCH_LIBS([log10], [m])
AC_SEARCH_LIBS([lrint], [m])
AC_SEARCH_LIBS([fabs], [m])
@ -118,6 +122,7 @@ FORK_FUNC_REQUIRE([COMMON], [GNU libunistring], [LIBUNISTRING], [unistring],
FORK_MODULES_CHECK([FORKED], [ZLIB], [zlib], [deflate], [zlib.h])
FORK_MODULES_CHECK([FORKED], [CONFUSE], [libconfuse >= 3.0], [cfg_init], [confuse.h])
FORK_MODULES_CHECK([FORKED], [LIBCURL], [libcurl], [curl_global_init], [curl/curl.h])
FORK_MODULES_CHECK([FORKED], [LIBSODIUM], [libsodium], [sodium_init], [sodium.h])
FORK_MODULES_CHECK([FORKED], [MINIXML], [mxml],
[mxmlNewElement], [mxml.h],
@ -162,6 +167,14 @@ FORK_MODULES_CHECK([FORKED], [JSON_C], [json-c],
[Define to 1 if you have json-c < 0.11])])
])
dnl Build with libplist (2.2.0 does not ship libplist.pc, only libplist-2.0.pc)
PKG_CHECK_EXISTS([libplist],
[FORK_MODULES_CHECK([FORKED], [LIBPLIST], [libplist >= 0.16],
[plist_dict_get_item], [plist/plist.h])],
[FORK_MODULES_CHECK([FORKED], [LIBPLIST], [libplist-2.0],
[plist_dict_get_item], [plist/plist.h])])
FORK_FUNC_REQUIRE([FORKED], [ANTLR3 C runtime], [ANTLR3C], [antlr3c],
[antlr3BaseRecognizerNew], [antlr3.h],
[AC_CHECK_FUNC([[antlr3NewAsciiStringInPlaceStream]],
@ -178,6 +191,7 @@ AM_PATH_GPG_ERROR([1.6])
FORK_FUNC_REQUIRE([FORKED], [GNUPG Error Values], [GPG_ERROR_MT], [gpg-error],
[gpg_err_init], [gpg-error.h])
AC_CHECK_HEADER([sys/eventfd.h], [AC_CHECK_FUNCS([eventfd])])
AC_CHECK_HEADER([sys/timerfd.h], [AC_CHECK_FUNC([timerfd_create],
@ -258,17 +272,6 @@ FORK_ARG_WITH_CHECK([FORKED_OPTS], [libwebsockets support], [libwebsockets], [LI
[libwebsockets >= 2.0.2])
AM_CONDITIONAL([COND_LIBWEBSOCKETS], [[test "x$with_libwebsockets" = "xyes"]])
dnl Build with libsodium
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libsodium support], [libsodium], [LIBSODIUM],
[libsodium], [sodium_init], [sodium.h])
dnl Build with libplist (2.2.0 does not ship libplist.pc, only libplist-2.0.pc)
PKG_CHECK_EXISTS([libplist],
[FORK_ARG_WITH_CHECK([FORKED_OPTS], [libplist support], [libplist], [LIBPLIST],
[libplist >= 0.16], [plist_dict_get_item], [plist/plist.h])],
[FORK_ARG_WITH_CHECK([FORKED_OPTS], [libplist support], [libplist], [LIBPLIST],
[libplist-2.0], [plist_dict_get_item], [plist/plist.h])])
dnl Build with libevent_pthreads
FORK_ARG_WITH_CHECK([FORKED_OPTS], [libevent_pthreads support],
[libevent_pthreads], [LIBEVENT_PTHREADS], [libevent_pthreads],
@ -323,30 +326,16 @@ FORK_ARG_ENABLE([Chromecast support], [chromecast], [CHROMECAST],
AM_CONDITIONAL([COND_CHROMECAST], [[test "x$enable_chromecast" = "xyes"]])
AM_CONDITIONAL([COND_PROTOBUF_OLD], [[test "x$protobuf_old" = "xyes"]])
dnl Prefer AirPlay 2
FORK_ARG_ENABLE([preference for AirPlay 2 for devices that support both 1 and 2], [preferairplay2], [PREFER_AIRPLAY2])
dnl DB profiling support
FORK_ARG_ENABLE([DB profiling support], [dbprofile], [DB_PROFILE])
dnl iTunes playlists with libplist
FORK_ARG_DISABLE([iTunes Music Library XML support], [itunes], [ITUNES],
[AS_IF([[test "x$with_libplist" = "xno"]],
[AC_MSG_ERROR([[iTunes Music Library XML support requires libplist]])])
])
AM_CONDITIONAL([COND_ITUNES], [[test "x$enable_itunes" = "xyes"]])
dnl MPD support
FORK_ARG_DISABLE([MPD client protocol support], [mpd], [MPD])
AM_CONDITIONAL([COND_MPD], [[test "x$enable_mpd" = "xyes"]])
dnl Apple device verification
FORK_ARG_DISABLE([Apple TV device verification], [verification], [RAOP_VERIFICATION],
[
AS_IF([[test "x$with_libsodium" = "xno"]],
[AC_MSG_ERROR([[Apple TV device verification requires libsodium]])])
AS_IF([[test "x$with_libplist" = "xno"]],
[AC_MSG_ERROR([[Apple TV device verification requires libplist]])])
])
AM_CONDITIONAL([COND_RAOP_VERIFICATION], [[test "x$enable_verification" = "xyes"]])
dnl Include default webinterface
FORK_ARG_DISABLE([Include default web interface], [webinterface], [WEBINTERFACE],
[AS_IF([[test "x$with_libwebsockets" = "xno"]],

View File

@ -21,7 +21,8 @@ Debug domains; available domains are: \fIconfig\fP, \fIdaap\fP,
\fIrsp\fP, \fIscan\fP, \fIxcode\fP, \fIevent\fP, \fIhttp\fP, \fIremote\fP,
\fIdacp\fP, \fIffmpeg\fP, \fIartwork\fP, \fIplayer\fP, \fIraop\fP,
\fIlaudio\fP, \fIdmap\fP, \fIfdbperf\fP, \fIspotify\fP, \fIlastfm\fP,
\fIcache\fP, \fImpd\fP, \fIstream\fP, \fIcast\fP, \fIfifo\fP, \fIlib\fP.
\fIcache\fP, \fImpd\fP, \fIstream\fP, \fIcast\fP, \fIfifo\fP, \fIlib\fP,
\fIweb\fP, \fIairplay\fP.
.TP
\fB\-c, \-\-config=\fR\fIfile\fP
Use \fIfile\fP as the configuration file.

View File

@ -1,10 +1,6 @@
sbin_PROGRAMS = forked-daapd
if COND_ITUNES
ITUNES_SRC=library/filescanner_itunes.c
endif
if COND_SPOTIFY
SPOTIFY_SRC=spotify.c spotify.h spotify_webapi.c spotify_webapi.h inputs/spotify.c
endif
@ -25,9 +21,8 @@ if COND_MPD
MPD_SRC=mpd.c mpd.h
endif
if COND_RAOP_VERIFICATION
RAOP_VERIFICATION_SRC=outputs/raop_verification.c outputs/raop_verification.h
endif
PAIR_AP_SRC=outputs/pair_fruit.c outputs/pair_homekit.c outputs/pair.c outputs/pair-internal.h outputs/pair.h
AM_CPPFLAGS += -DCONFIG_GCRYPT
if COND_ALSA
ALSA_SRC=outputs/alsa.c
@ -102,7 +97,7 @@ forked_daapd_SOURCES = main.c \
cache.c cache.h \
library/filescanner.c library/filescanner.h \
library/filescanner_ffmpeg.c library/filescanner_playlist.c \
library/filescanner_smartpl.c $(ITUNES_SRC) \
library/filescanner_smartpl.c library/filescanner_itunes.c \
library/rssscanner.c \
library.c library.h \
$(MDNS_SRC) mdns.h \
@ -133,7 +128,7 @@ forked_daapd_SOURCES = main.c \
inputs/file.c inputs/http.c inputs/pipe.c inputs/timer.c \
outputs.h outputs.c \
outputs/rtp_common.h outputs/rtp_common.c \
outputs/raop.c $(RAOP_VERIFICATION_SRC) \
outputs/raop.c outputs/airplay.c $(PAIR_AP_SRC) \
outputs/streaming.c outputs/dummy.c outputs/fifo.c \
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
@ -143,6 +138,7 @@ forked_daapd_SOURCES = main.c \
listener.c listener.h \
commands.c commands.h \
mxml-compat.h \
outputs/plist_wrap.h \
$(LIBWEBSOCKETS_SRC) \
$(GPERF_SRC) \
$(ANTLR_SRC)

View File

@ -26,7 +26,7 @@
* is a major upgrade. In other words minor version upgrades permit downgrading
* forked-daapd after the database was upgraded. */
#define SCHEMA_VERSION_MAJOR 21
#define SCHEMA_VERSION_MINOR 04
#define SCHEMA_VERSION_MINOR 05
int
db_init_indices(sqlite3 *hdl);

View File

@ -1060,6 +1060,19 @@ static const struct db_upgrade_query db_upgrade_v2104_queries[] =
{ U_v2104_SCVER_MINOR, "set schema_version_minor to 04" },
};
// Previously, the auth_key contained the public key twice
#define U_v2105_UPDATE_SPEAKERS_AUTH_KEY \
"UPDATE speakers SET auth_key = SUBSTR(auth_key, LENGTH(auth_key) - 128 + 1, LENGTH(auth_key) + 1) WHERE LENGTH(auth_key) = 128 + 64;"
#define U_v2105_SCVER_MINOR \
"UPDATE admin SET value = '05' WHERE key = 'schema_version_minor';"
static const struct db_upgrade_query db_upgrade_v2105_queries[] =
{
{ U_v2105_UPDATE_SPEAKERS_AUTH_KEY, "update table speakers auth_key length" },
{ U_v2105_SCVER_MINOR, "set schema_version_minor to 05" },
};
int
db_upgrade(sqlite3 *hdl, int db_ver)
@ -1237,6 +1250,14 @@ db_upgrade(sqlite3 *hdl, int db_ver)
return -1;
break;
/* FALLTHROUGH */
case 2104:
ret = db_generic_upgrade(hdl, db_upgrade_v2105_queries, ARRAY_SIZE(db_upgrade_v2105_queries));
if (ret < 0)
return -1;
break;
default:
DPRINTF(E_FATAL, L_DB, "No upgrade path from the current DB schema\n");
return -1;

View File

@ -45,8 +45,9 @@ extern "C" {
/* Response codes */
#define RTSP_OK 200
#define RTSP_UNAUTHORIZED 401
#define RTSP_FORBIDDEN 403
#define RTSP_UNAUTHORIZED 401
#define RTSP_FORBIDDEN 403
#define RTSP_CONNECTION_AUTH_REQUIRED 470
struct evrtsp_connection;
@ -64,6 +65,8 @@ enum evrtsp_cmd_type {
EVRTSP_REQ_FLUSH,
EVRTSP_REQ_TEARDOWN,
EVRTSP_REQ_POST,
EVRTSP_REQ_GET,
EVRTSP_REQ_SETPEERS,
};
enum evrtsp_request_kind { EVRTSP_REQUEST, EVRTSP_RESPONSE };
@ -133,6 +136,10 @@ void evrtsp_connection_free(struct evrtsp_connection *evcon);
void evrtsp_connection_set_closecb(struct evrtsp_connection *evcon,
void (*)(struct evrtsp_connection *, void *), void *);
/** Set a callback for encryption/decryption. */
void evrtsp_connection_set_ciphercb(struct evrtsp_connection *evcon,
void (*)(struct evbuffer *, void *, int encrypt), void *);
/**
* Associates an event base with the connection - can only be called
* on a freshly created connection object that has not been used yet.

View File

@ -85,6 +85,9 @@ struct evrtsp_connection {
void (*closecb)(struct evrtsp_connection *, void *);
void *closecb_arg;
void (*ciphercb)(struct evbuffer *evbuf, void *, int encrypt);
void *ciphercb_arg;
struct event_base *base;
};

View File

@ -264,6 +264,14 @@ evrtsp_method(enum evrtsp_cmd_type type)
method = "POST";
break;
case EVRTSP_REQ_GET:
method = "GET";
break;
case EVRTSP_REQ_SETPEERS:
method = "SETPEERS";
break;
default:
method = NULL;
break;
@ -612,6 +620,9 @@ evrtsp_read(int fd, short what, void *arg)
return;
}
if (evcon->ciphercb)
evcon->ciphercb(evcon->input_buffer, evcon->ciphercb_arg, 0);
switch (evcon->state) {
case EVCON_READING_FIRSTLINE:
evrtsp_read_firstline(evcon, req);
@ -717,6 +728,10 @@ evrtsp_request_dispatch(struct evrtsp_connection* evcon)
/* Create the header from the store arguments */
evrtsp_make_header(evcon, req);
/* forked-daapd customisation for encryption */
if (evcon->ciphercb)
evcon->ciphercb(evcon->output_buffer, evcon->ciphercb_arg, 1);
evrtsp_write_buffer(evcon, evrtsp_write_connectioncb, NULL);
}
@ -1304,6 +1319,14 @@ evrtsp_connection_set_closecb(struct evrtsp_connection *evcon,
evcon->closecb_arg = cbarg;
}
void
evrtsp_connection_set_ciphercb(struct evrtsp_connection *evcon,
void (*cb)(struct evbuffer *, void *, int encrypt), void *cbarg)
{
evcon->ciphercb = cb;
evcon->ciphercb_arg = cbarg;
}
void
evrtsp_connection_get_local_address(struct evrtsp_connection *evcon,
char **address, u_short *port, int *family)

View File

@ -30,6 +30,7 @@
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <ctype.h> // for isprint()
#include <event2/event.h>
@ -57,7 +58,7 @@ static uint32_t logger_repeat_counter;
static uint32_t logger_last_hash;
static char *logfilename;
static FILE *logfile;
static char *labels[] = { "config", "daap", "db", "httpd", "http", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf", "spotify", "lastfm", "cache", "mpd", "stream", "cast", "fifo", "lib", "web" };
static char *labels[] = { "config", "daap", "db", "httpd", "http", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf", "spotify", "lastfm", "cache", "mpd", "stream", "cast", "fifo", "lib", "web", "airplay" };
static char *severities[] = { "FATAL", "LOG", "WARN", "INFO", "DEBUG", "SPAM" };
@ -110,14 +111,48 @@ repeat_count(const char *fmt)
return logger_repeat_counter;
}
static void
logger_write(const char *fmt, ...)
{
va_list ap;
if (logfile)
{
va_start(ap, fmt);
vfprintf(logfile, fmt, ap);
va_end(ap);
fflush(logfile);
}
if (console)
{
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
}
static void
logger_write_with_label(int severity, int domain, const char *content)
{
char stamp[32];
time_t t;
struct tm timebuf;
int ret;
t = time(NULL);
ret = strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime_r(&t, &timebuf));
if (ret == 0)
stamp[0] = '\0';
logger_write("[%s] [%5s] %8s: %s", stamp, severities[severity], labels[domain], content);
}
static void
vlogger_writer(int severity, int domain, const char *fmt, va_list args)
{
va_list ap;
char content[2048];
char stamp[32];
time_t t;
struct tm timebuf;
int ret;
va_copy(ap, args);
@ -134,22 +169,7 @@ vlogger_writer(int severity, int domain, const char *fmt, va_list args)
else if (ret > LOGGER_REPEAT_MAX)
return;
if (logfile)
{
t = time(NULL);
ret = strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime_r(&t, &timebuf));
if (ret == 0)
stamp[0] = '\0';
fprintf(logfile, "[%s] [%5s] %8s: %s", stamp, severities[severity], labels[domain], content);
fflush(logfile);
}
if (console)
{
fprintf(stderr, "[%5s] %8s: %s", severities[severity], labels[domain], content);
}
logger_write_with_label(severity, domain, content);
}
static void
@ -189,6 +209,52 @@ vlogger(int severity, int domain, const char *fmt, va_list args)
LOGGER_CHECK_ERR(pthread_mutex_unlock(&logger_lck));
}
static void
hexdump(int severity, int domain, const unsigned char *data, int len, const char *heading)
{
int i;
unsigned char buff[17];
const unsigned char *pc = data;
if (len <= 0)
return;
LOGGER_CHECK_ERR(pthread_mutex_lock(&logger_lck));
if (heading)
logger_write_with_label(severity, domain, heading);
for (i = 0; i < len; i++)
{
if ((i % 16) == 0)
{
if (i != 0)
logger_write(" %s\n", buff);
logger_write(" %04x ", i);
}
logger_write(" %02x", pc[i]);
if (isprint(pc[i]))
buff[i % 16] = pc[i];
else
buff[i % 16] = '.';
buff[(i % 16) + 1] = '\0';
}
while ((i % 16) != 0)
{
logger_write(" ");
i++;
}
logger_write(" %s\n", buff);
LOGGER_CHECK_ERR(pthread_mutex_unlock(&logger_lck));
}
void
DPRINTF(int severity, int domain, const char *fmt, ...)
{
@ -215,6 +281,17 @@ DVPRINTF(int severity, int domain, const char *fmt, va_list ap)
vlogger(severity, domain, fmt, ap);
}
void
DHEXDUMP(int severity, int domain, const unsigned char *data, int data_len, const char *heading)
{
// If domain and severity do not match the current log configuration, return early to
// save some unnecessary code execution (tiny performance gain)
if (logger_initialized && (!((1 << domain) & logdomains) || (severity > threshold)))
return;
hexdump(severity, domain, data, data_len, heading);
}
void
logger_ffmpeg(void *ptr, int level, const char *fmt, va_list ap)
{

View File

@ -36,8 +36,9 @@
#define L_FIFO 27
#define L_LIB 28
#define L_WEB 29
#define L_AIRPLAY 30
#define N_LOGDOMAINS 30
#define N_LOGDOMAINS 31
/* Severities */
#define E_FATAL 0
@ -55,6 +56,9 @@ DPRINTF(int severity, int domain, const char *fmt, ...) __attribute__((format(pr
void
DVPRINTF(int severity, int domain, const char *fmt, va_list ap);
void
DHEXDUMP(int severity, int domain, const unsigned char *data, int data_len, const char *heading);
void
logger_ffmpeg(void *ptr, int level, const char *fmt, va_list ap);

View File

@ -34,11 +34,15 @@
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <limits.h>
#include <sys/param.h>
#ifndef CLOCK_REALTIME
#include <sys/time.h>
#endif
#ifdef HAVE_UUID
#include <uuid/uuid.h>
#endif
#include <unistr.h>
#include <uniconv.h>
@ -57,11 +61,6 @@ static char *buildopts[] =
#else
"libav",
#endif
#ifdef ITUNES
"iTunes XML",
#else
"Without iTunes XML",
#endif
#ifdef SPOTIFY
"Spotify",
#else
@ -82,11 +81,6 @@ static char *buildopts[] =
#else
"Without MPD",
#endif
#ifdef RAOP_VERIFICATION
"Device verification",
#else
"Without device verification",
#endif
#ifdef HAVE_LIBWEBSOCKETS
"Websockets",
#else
@ -1021,6 +1015,46 @@ murmur_hash64(const void *key, int len, uint32_t seed)
# error Platform not supported
#endif
#ifdef HAVE_UUID
void
uuid_make(char *str)
{
uuid_t uu;
uuid_generate_random(uu);
uuid_unparse_upper(uu, str);
}
#else
void
uuid_make(char *str)
{
uint16_t uuid[8];
time_t now;
int i;
now = time(NULL);
srand((unsigned int)now);
for (i = 0; i < ARRAY_SIZE(uuid); i++)
{
uuid[i] = (uint16_t)rand();
// time_hi_and_version, set version to 4 (=random)
if (i == 3)
uuid[i] = (uuid[i] & 0x0FFF) | 0x4000;
// clock_seq, variant 1
if (i == 4)
uuid[i] = (uuid[i] & 0x3FFF) | 0x8000;
if (i == 2 || i == 3 || i == 4 || i == 5)
str += sprintf(str, "-");
str += sprintf(str, "%04" PRIX16, uuid[i]);
}
}
#endif
int
linear_regression(double *m, double *b, double *r2, const double *x, const double *y, int n)

View File

@ -150,6 +150,9 @@ b64_encode(const uint8_t *src, int srclen);
uint64_t
murmur_hash64(const void *key, int len, uint32_t seed);
void
uuid_make(char *str);
int
linear_regression(double *m, double *b, double *r, const double *x, const double *y, int n);

View File

@ -40,6 +40,7 @@
#include "outputs.h"
extern struct output_definition output_raop;
extern struct output_definition output_airplay;
extern struct output_definition output_streaming;
extern struct output_definition output_dummy;
extern struct output_definition output_fifo;
@ -59,6 +60,7 @@ extern struct event_base *evbase_player;
// Must be in sync with enum output_types
static struct output_definition *outputs[] = {
&output_raop,
&output_airplay,
&output_streaming,
&output_dummy,
&output_fifo,
@ -701,6 +703,21 @@ outputs_device_add(struct output_device *add, bool new_deselect)
break;
}
// This is relevant for Airplay 1 and 2 where the same device can support both
if (device && device->type != add->type)
{
if (outputs_priority(device) < outputs_priority(add))
{
DPRINTF(E_DBG, L_PLAYER, "Ignoring type %s for device '%s', will use type %s\n", add->type_name, add->name, device->type_name);
outputs_device_free(add);
return NULL;
}
// Remove existing device, higher priority device will be added below
outputs_device_remove(device);
device = NULL;
}
// New device
if (!device)
{
@ -799,7 +816,7 @@ outputs_device_remove(struct output_device *remove)
if (ret < 0)
DPRINTF(E_LOG, L_PLAYER, "Could not save state for %s device '%s'\n", remove->type_name, remove->name);
DPRINTF(E_INFO, L_PLAYER, "Removing %s device '%s'; stopped advertising\n", remove->type_name, remove->name);
DPRINTF(E_INFO, L_PLAYER, "Removing %s device '%s'\n", remove->type_name, remove->name);
if (!prev)
outputs_device_list = remove->next;
@ -1226,6 +1243,11 @@ outputs_init(void)
return -1;
}
if (outputs[i]->disabled)
{
continue;
}
if (!outputs[i]->init)
{
no_output = 0;

View File

@ -59,6 +59,7 @@ typedef int (*output_metadata_finalize_cb)(struct output_metadata *metadata);
enum output_types
{
OUTPUT_TYPE_RAOP,
OUTPUT_TYPE_AIRPLAY,
OUTPUT_TYPE_STREAMING,
OUTPUT_TYPE_DUMMY,
OUTPUT_TYPE_FIFO,
@ -194,7 +195,9 @@ struct output_definition
// Type of output
enum output_types type;
// Priority to give this output when autoselecting an output, 1 is highest
// Priority to give this output when autoselecting an output, or when
// selectinga which output definition to use for a device that has multiple,
// e.g. AirPlay 1 and 2.
// 1 = highest priority, 0 = don't autoselect
int priority;
@ -284,7 +287,7 @@ outputs_cb(int callback_id, uint64_t device_id, enum output_device_state);
// Ownership of *add is transferred, so don't address after calling. Instead you
// can address the return value (which is not the same if the device was already
// in the list.
// in the list).
struct output_device *
outputs_device_add(struct output_device *add, bool new_deselect);

4452
src/outputs/airplay.c Normal file

File diff suppressed because it is too large Load Diff

240
src/outputs/pair-internal.h Normal file
View File

@ -0,0 +1,240 @@
#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;
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);
int (*pair_encrypt)(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
int (*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

578
src/outputs/pair.c Normal file
View File

@ -0,0 +1,578 @@
/*
* 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;
}
int
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);
}
int
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--;
}
void
pair_decrypt_rollback(struct pair_cipher_context *cctx)
{
cctx->decryption_counter--;
}

117
src/outputs/pair.h Normal file
View File

@ -0,0 +1,117 @@
#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);
int
pair_encrypt(uint8_t **ciphertext, size_t *ciphertext_len, uint8_t *plaintext, size_t plaintext_len, struct pair_cipher_context *cctx);
int
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__ */

View File

@ -1,22 +1,21 @@
/*
*
* The Secure Remote Password 6a implementation included here is by
* The Secure Remote Password 6a implementation is adapted from:
* - Tom Cocagne
* <https://github.com/cocagne/csrp>
*
*
* 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
@ -24,7 +23,7 @@
* 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>
@ -34,74 +33,7 @@
#include <plist/plist.h>
#include <sodium.h>
#include "raop_verification.h"
#define CONFIG_GCRYPT 1
/* -------------------- 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;
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;
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);
}
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
#include "pair-internal.h"
/* ----------------------------- DEFINES ETC ------------------------------- */
@ -113,74 +45,6 @@ static void bnum_modexp(bnum bn, bnum y, bnum q, bnum p)
#define AES_VERIFY_KEY "Pair-Verify-AES-Key"
#define AES_VERIFY_IV "Pair-Verify-AES-IV"
#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
struct verification_setup_context
{
struct SRPUser *user;
char pin[4];
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;
const char *errmsg;
};
struct verification_verify_context
{
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];
const char *errmsg;
};
/* ---------------------------------- SRP ---------------------------------- */
@ -196,40 +60,29 @@ typedef struct
bnum g;
} NGConstant;
#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
struct SRPUser
{
enum hash_alg alg;
NGConstant *ng;
bnum a;
bnum A;
bnum S;
const unsigned char *bytes_A;
int authenticated;
const char *username;
const unsigned char *password;
int password_len;
int authenticated;
char *username;
unsigned char *password;
int password_len;
unsigned char M [SHA512_DIGEST_LENGTH];
unsigned char H_AMK [SHA512_DIGEST_LENGTH];
unsigned char session_key [2 * SHA512_DIGEST_LENGTH]; // See hash_session_key()
int session_key_len;
};
struct NGHex
struct NGHex
{
const char *n_hex;
const char *g_hex;
@ -263,10 +116,10 @@ new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex)
n_hex = global_Ng_constants[ ng_type ].n_hex;
g_hex = global_Ng_constants[ ng_type ].g_hex;
}
bnum_hex2bn(ng->N, n_hex);
bnum_hex2bn(ng->g, g_hex);
return ng;
}
@ -281,167 +134,6 @@ free_ng(NGConstant * ng)
free(ng);
}
static 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
}
static 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
}
static 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
}
static 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
}
static 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
}
static 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);
}
static 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;
}
static 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;
}
static bnum
calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsigned char *password, int password_len)
{
@ -453,30 +145,10 @@ calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsi
hash_update( alg, &ctx, ":", 1 );
hash_update( alg, &ctx, password, password_len );
hash_final( alg, &ctx, ucp_hash );
return H_ns( alg, salt, ucp_hash, hash_length(alg) );
}
static 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);
}
static 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);
}
static int
hash_session_key(enum hash_alg alg, const bnum n, unsigned char *dest)
{
@ -508,24 +180,24 @@ calculate_M(enum hash_alg alg, NGConstant *ng, unsigned char *dest, const char *
HashCTX ctx;
int i = 0;
int hash_len = hash_length(alg);
hash_num( alg, ng->N, H_N );
hash_num( alg, ng->g, H_g );
hash(alg, (const unsigned char *)I, strlen(I), H_I);
for (i = 0; i < hash_len; i++)
for (i=0; i < hash_len; i++ )
H_xor[i] = H_N[i] ^ H_g[i];
hash_init( alg, &ctx );
hash_update( alg, &ctx, H_xor, hash_len );
hash_update( alg, &ctx, H_I, hash_len );
update_hash_n( alg, &ctx, s );
update_hash_n( alg, &ctx, A );
update_hash_n( alg, &ctx, B );
hash_update( alg, &ctx, K, K_len );
hash_final( alg, &ctx, dest );
}
@ -533,18 +205,18 @@ static void
calculate_H_AMK(enum hash_alg alg, unsigned char *dest, const bnum A, const unsigned char * M, const unsigned char * K, int K_len)
{
HashCTX ctx;
hash_init( alg, &ctx );
update_hash_n( alg, &ctx, A );
hash_update( alg, &ctx, M, hash_length(alg) );
hash_update( alg, &ctx, K, K_len );
hash_final( alg, &ctx, dest );
}
static struct SRPUser *
srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
const unsigned char *bytes_password, int len_password,
const char *n_hex, const char *g_hex)
{
@ -556,27 +228,27 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
usr->alg = alg;
usr->ng = new_ng( ng_type, n_hex, g_hex );
bnum_new(usr->a);
bnum_new(usr->A);
bnum_new(usr->S);
if (!usr->ng || !usr->a || !usr->A || !usr->S)
goto err_exit;
usr->username = (const char *) malloc(ulen);
usr->password = (const unsigned char *) malloc(len_password);
usr->username = malloc(ulen);
usr->password = malloc(len_password);
usr->password_len = len_password;
if (!usr->username || !usr->password)
goto err_exit;
memcpy((char *)usr->username, username, ulen);
memcpy((char *)usr->password, bytes_password, len_password);
memcpy(usr->username, username, ulen);
memcpy(usr->password, bytes_password, len_password);
usr->authenticated = 0;
usr->bytes_A = 0;
return usr;
err_exit:
@ -586,12 +258,12 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
bnum_free(usr->a);
bnum_free(usr->A);
bnum_free(usr->S);
if (usr->username)
free((void*)usr->username);
free(usr->username);
if (usr->password)
{
memset((void*)usr->password, 0, usr->password_len);
free((void*)usr->password);
memset(usr->password, 0, usr->password_len);
free(usr->password);
}
free(usr);
@ -599,7 +271,7 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
}
static void
srp_user_delete(struct SRPUser *usr)
srp_user_free(struct SRPUser *usr)
{
if(!usr)
return;
@ -607,16 +279,14 @@ srp_user_delete(struct SRPUser *usr)
bnum_free(usr->a);
bnum_free(usr->A);
bnum_free(usr->S);
free_ng(usr->ng);
memset((void*)usr->password, 0, usr->password_len);
free((char *)usr->username);
free((char *)usr->password);
if (usr->bytes_A)
free( (char *)usr->bytes_A );
memset(usr->password, 0, usr->password_len);
free(usr->username);
free(usr->password);
free((char *)usr->bytes_A);
memset(usr, 0, sizeof(*usr));
free(usr);
@ -643,7 +313,7 @@ srp_user_start_authentication(struct SRPUser *usr, const char **username,
{
bnum_random(usr->a, 256);
bnum_modexp(usr->A, usr->ng->g, usr->a, usr->ng->N);
*len_A = bnum_num_bytes(usr->A);
*bytes_A = malloc(*len_A);
@ -654,9 +324,9 @@ srp_user_start_authentication(struct SRPUser *usr, const char **username,
*username = 0;
return;
}
bnum_bn2bin(usr->A, (unsigned char *) *bytes_A, *len_A);
usr->bytes_A = *bytes_A;
*username = usr->username;
}
@ -694,7 +364,7 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in
if (!bnum_is_zero(B) && !bnum_is_zero(u))
{
bnum_modexp(v, usr->ng->g, x, usr->ng->N);
// S = (B - k*(g^x)) ^ (a + ux)
bnum_mul(tmp1, u, x);
bnum_add(tmp2, usr->a, tmp1); // tmp2 = (a + ux)
@ -704,17 +374,19 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in
bnum_modexp(usr->S, tmp1, tmp2, usr->ng->N);
usr->session_key_len = hash_session_key(usr->alg, usr->S, usr->session_key);
calculate_M(usr->alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key, usr->session_key_len);
calculate_H_AMK(usr->alg, usr->H_AMK, usr->A, usr->M, usr->session_key, usr->session_key_len);
*bytes_M = usr->M;
*len_M = hash_length(usr->alg);
if (len_M)
*len_M = hash_length(usr->alg);
}
else
{
*bytes_M = NULL;
*len_M = 0;
if (len_M)
*len_M = 0;
}
cleanup2:
@ -920,32 +592,37 @@ encrypt_ctr(unsigned char *ciphertext, int ciphertext_len,
}
/* ---------------------------------- API ---------------------------------- */
/* -------------------------- IMPLEMENTATION -------------------------------- */
struct verification_setup_context *
verification_setup_new(const char *pin)
static struct pair_setup_context *
pair_setup_new(struct pair_definition *type, const char *pin, const char *device_id)
{
struct verification_setup_context *sctx;
struct pair_setup_context *sctx;
if (sodium_init() == -1)
return NULL;
sctx = calloc(1, sizeof(struct verification_setup_context));
if (!pin || strlen(pin) < 4)
return NULL;
sctx = calloc(1, sizeof(struct pair_setup_context));
if (!sctx)
return NULL;
sctx->type = type;
memcpy(sctx->pin, pin, sizeof(sctx->pin));
return sctx;
}
void
verification_setup_free(struct verification_setup_context *sctx)
static void
pair_setup_free(struct pair_setup_context *sctx)
{
if (!sctx)
return;
srp_user_delete(sctx->user);
srp_user_free(sctx->user);
free(sctx->pkB);
free(sctx->M2);
@ -956,18 +633,13 @@ verification_setup_free(struct verification_setup_context *sctx)
free(sctx);
}
const char *
verification_setup_errmsg(struct verification_setup_context *sctx)
{
return sctx->errmsg;
}
uint8_t *
verification_setup_request1(uint32_t *len, struct verification_setup_context *sctx)
static uint8_t *
pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
{
plist_t dict;
plist_t method;
plist_t user;
uint32_t uint32;
char *data = NULL; // Necessary to initialize because plist_to_bin() uses value
sctx->user = srp_user_new(HASH_SHA1, SRP_NG_2048, USERNAME, (unsigned char *)sctx->pin, sizeof(sctx->pin), 0, 0);
@ -979,19 +651,21 @@ verification_setup_request1(uint32_t *len, struct verification_setup_context *sc
plist_dict_set_item(dict, "method", method);
plist_dict_set_item(dict, "user", user);
plist_to_bin(dict, &data, len);
plist_to_bin(dict, &data, &uint32);
plist_free(dict);
*len = (size_t)uint32;
return (uint8_t *)data;
}
uint8_t *
verification_setup_request2(uint32_t *len, struct verification_setup_context *sctx)
static uint8_t *
pair_setup_request2(size_t *len, struct pair_setup_context *sctx)
{
plist_t dict;
plist_t pk;
plist_t proof;
const char *auth_username = NULL;
uint32_t uint32;
char *data = NULL;
// Calculate A
@ -1006,18 +680,20 @@ verification_setup_request2(uint32_t *len, struct verification_setup_context *sc
dict = plist_new_dict();
plist_dict_set_item(dict, "pk", pk);
plist_dict_set_item(dict, "proof", proof);
plist_to_bin(dict, &data, len);
plist_to_bin(dict, &data, &uint32);
plist_free(dict);
*len = (size_t)uint32;
return (uint8_t *)data;
}
uint8_t *
verification_setup_request3(uint32_t *len, struct verification_setup_context *sctx)
static uint8_t *
pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
{
plist_t dict;
plist_t epk;
plist_t authtag;
uint32_t uint32;
char *data = NULL;
const unsigned char *session_key;
int session_key_len;
@ -1049,7 +725,7 @@ verification_setup_request3(uint32_t *len, struct verification_setup_context *sc
return NULL;
}
iv[15]++; // Magic
iv[15]++; // Nonce?
/*
if (iv[15] == 0x00 || iv[15] == 0xff)
printf("- note that value of last byte is %d!\n", iv[15]);
@ -1069,14 +745,15 @@ verification_setup_request3(uint32_t *len, struct verification_setup_context *sc
dict = plist_new_dict();
plist_dict_set_item(dict, "epk", epk);
plist_dict_set_item(dict, "authTag", authtag);
plist_to_bin(dict, &data, len);
plist_to_bin(dict, &data, &uint32);
plist_free(dict);
*len = (size_t)uint32;
return (uint8_t *)data;
}
int
verification_setup_response1(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len)
static int
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
{
plist_t dict;
plist_t pk;
@ -1101,8 +778,8 @@ verification_setup_response1(struct verification_setup_context *sctx, const uint
return 0;
}
int
verification_setup_response2(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len)
static int
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
{
plist_t dict;
plist_t proof;
@ -1132,8 +809,8 @@ verification_setup_response2(struct verification_setup_context *sctx, const uint
return 0;
}
int
verification_setup_response3(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len)
static int
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
{
plist_t dict;
plist_t epk;
@ -1163,92 +840,28 @@ verification_setup_response3(struct verification_setup_context *sctx, const uint
plist_free(dict);
sctx->setup_is_completed = 1;
return 0;
}
int
verification_setup_result(const char **authorisation_key, struct verification_setup_context *sctx)
static int
pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx)
{
struct verification_verify_context *vctx;
char *ptr;
int i;
if (sizeof(vctx->client_public_key) != sizeof(sctx->public_key) || sizeof(vctx->client_private_key) != sizeof(sctx->private_key))
// Last 32 bytes of private key should match public key, but check assumption
if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0)
{
sctx->errmsg = "Setup result: Bug!";
sctx->errmsg = "Pair setup result: Unexpected keys, private key does not match public key";
return -1;
}
// Fills out the auth_key with public + private in hex. It seems that the private
// key actually includes the public key (last 32 bytes), so we could in
// principle just export the private key
ptr = sctx->auth_key;
for (i = 0; i < sizeof(sctx->public_key); i++)
ptr += sprintf(ptr, "%02x", sctx->public_key[i]);
for (i = 0; i < sizeof(sctx->private_key); i++)
ptr += sprintf(ptr, "%02x", sctx->private_key[i]);
*ptr = '\0';
*authorisation_key = sctx->auth_key;
*key = sctx->private_key;
*key_len = sizeof(sctx->private_key);
return 0;
}
struct verification_verify_context *
verification_verify_new(const char *authorisation_key)
{
struct verification_verify_context *vctx;
char hex[] = { 0, 0, 0 };
const char *ptr;
int i;
if (sodium_init() == -1)
return NULL;
if (!authorisation_key)
return NULL;
if (strlen(authorisation_key) != 2 * (sizeof(vctx->client_public_key) + sizeof(vctx->client_private_key)))
return NULL;
vctx = calloc(1, sizeof(struct verification_verify_context));
if (!vctx)
return NULL;
ptr = authorisation_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);
}
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);
}
return vctx;
}
void
verification_verify_free(struct verification_verify_context *vctx)
{
if (!vctx)
return;
free(vctx);
}
const char *
verification_verify_errmsg(struct verification_verify_context *vctx)
{
return vctx->errmsg;
}
uint8_t *
verification_verify_request1(uint32_t *len, struct verification_verify_context *vctx)
static uint8_t *
pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
{
const uint8_t basepoint[32] = {9};
uint8_t *data;
@ -1276,8 +889,8 @@ verification_verify_request1(uint32_t *len, struct verification_verify_context *
return data;
}
uint8_t *
verification_verify_request2(uint32_t *len, struct verification_verify_context *vctx)
static uint8_t *
pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
{
uint8_t shared_secret[crypto_scalarmult_BYTES];
uint8_t key[SHA512_DIGEST_LENGTH];
@ -1344,10 +957,10 @@ verification_verify_request2(uint32_t *len, struct verification_verify_context *
return data;
}
int
verification_verify_response1(struct verification_verify_context *vctx, const uint8_t *data, uint32_t data_len)
static int
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
{
uint32_t wanted;
size_t wanted;
wanted = sizeof(vctx->server_eph_public_key) + sizeof(vctx->server_public_key);
if (data_len < wanted)
@ -1361,3 +974,31 @@ verification_verify_response1(struct verification_verify_context *vctx, const ui
return 0;
}
static int
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
{
// TODO actually check response
return 0;
}
struct pair_definition pair_fruit =
{
.pair_setup_new = pair_setup_new,
.pair_setup_free = pair_setup_free,
.pair_setup_result = pair_setup_result,
.pair_setup_request1 = pair_setup_request1,
.pair_setup_request2 = pair_setup_request2,
.pair_setup_request3 = pair_setup_request3,
.pair_setup_response1 = pair_setup_response1,
.pair_setup_response2 = pair_setup_response2,
.pair_setup_response3 = pair_setup_response3,
.pair_verify_request1 = pair_verify_request1,
.pair_verify_request2 = pair_verify_request2,
.pair_verify_response1 = pair_verify_response1,
.pair_verify_response2 = pair_verify_response2,
};

1879
src/outputs/pair_homekit.c Normal file

File diff suppressed because it is too large Load Diff

66
src/outputs/plist_wrap.h Normal file
View File

@ -0,0 +1,66 @@
/*
* Convenience wrappers for libplist
*
*/
#include <plist/plist.h>
static void
wplist_dict_add_uint(plist_t node, const char *key, uint64_t val)
{
plist_t add = plist_new_uint(val);
plist_dict_set_item(node, key, add);
}
static void
wplist_dict_add_string(plist_t node, const char *key, const char *val)
{
plist_t add = plist_new_string(val);
plist_dict_set_item(node, key, add);
}
static void
wplist_dict_add_bool(plist_t node, const char *key, bool val)
{
plist_t add = plist_new_bool(val);
plist_dict_set_item(node, key, add);
}
static void
wplist_dict_add_data(plist_t node, const char *key, uint8_t *data, size_t len)
{
plist_t add = plist_new_data((const char *)data, len);
plist_dict_set_item(node, key, add);
}
static int
wplist_to_bin(uint8_t **data, size_t *len, plist_t node)
{
char *out = NULL;
uint32_t out_len = 0;
plist_to_bin(node, &out, &out_len);
if (!out)
return -1;
*data = (uint8_t *)out;
*len = out_len;
return 0;
}
static int
wplist_from_evbuf(plist_t *node, struct evbuffer *evbuf)
{
uint8_t *data = evbuffer_pullup(evbuf, -1);
size_t len = evbuffer_get_length(evbuf);
plist_t out = NULL;
plist_from_bin((char *)data, (uint32_t)len, &out);
if (!out)
return -1;
*node = out;
return 0;
}

View File

@ -64,10 +64,7 @@
#include "dmap_common.h"
#include "rtp_common.h"
#include "outputs.h"
#ifdef RAOP_VERIFICATION
#include "raop_verification.h"
#endif
#include "pair.h"
#define ALAC_HEADER_LEN 3
@ -222,11 +219,9 @@ struct raop_session
unsigned short control_port;
unsigned short timing_port; // ATV4 has this set to 0, but it is not used by forked-daapd anyway
#ifdef RAOP_VERIFICATION
/* Device verification, see raop_verification.h */
struct verification_verify_context *verification_verify_ctx;
struct verification_setup_context *verification_setup_ctx;
#endif
/* Device verification, see pair.h */
struct pair_verify_context *pair_verify_ctx;
struct pair_setup_context *pair_setup_ctx;
int server_fd;
@ -1658,7 +1653,6 @@ raop_send_req_options(struct raop_session *rs, evrtsp_req_cb cb, const char *log
return 0;
}
#ifdef RAOP_VERIFICATION
static int
raop_send_req_pin_start(struct raop_session *rs, evrtsp_req_cb cb, const char *log_caller)
{
@ -1696,14 +1690,6 @@ raop_send_req_pin_start(struct raop_session *rs, evrtsp_req_cb cb, const char *l
return 0;
}
#else
static int
raop_send_req_pin_start(struct raop_session *rs, evrtsp_req_cb cb, const char *log_caller)
{
DPRINTF(E_LOG, L_RAOP, "Device '%s' requires verification, but forked-daapd was built with --disable-verification\n", rs->devname);
return -1;
}
#endif
/* ------------------------------ Session handling -------------------------- */
@ -3987,9 +3973,8 @@ raop_cb_startup_options(struct evrtsp_request *req, void *arg)
/* ------------------------- tvOS device verification ----------------------- */
/* e.g. for the ATV4 (read it from the bottom and up) */
#ifdef RAOP_VERIFICATION
static int
raop_verification_response_process(int step, struct evrtsp_request *req, struct raop_session *rs)
raop_pair_response_process(int step, struct evrtsp_request *req, struct raop_session *rs)
{
uint8_t *response;
const char *errmsg;
@ -4016,20 +4001,20 @@ raop_verification_response_process(int step, struct evrtsp_request *req, struct
switch (step)
{
case 1:
ret = verification_setup_response1(rs->verification_setup_ctx, response, len);
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
ret = pair_setup_response1(rs->pair_setup_ctx, response, len);
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
break;
case 2:
ret = verification_setup_response2(rs->verification_setup_ctx, response, len);
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
ret = pair_setup_response2(rs->pair_setup_ctx, response, len);
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
break;
case 3:
ret = verification_setup_response3(rs->verification_setup_ctx, response, len);
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
ret = pair_setup_response3(rs->pair_setup_ctx, response, len);
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
break;
case 4:
ret = verification_verify_response1(rs->verification_verify_ctx, response, len);
errmsg = verification_verify_errmsg(rs->verification_verify_ctx);
ret = pair_verify_response1(rs->pair_verify_ctx, response, len);
errmsg = pair_verify_errmsg(rs->pair_verify_ctx);
break;
case 5:
ret = 0;
@ -4046,11 +4031,11 @@ raop_verification_response_process(int step, struct evrtsp_request *req, struct
}
static int
raop_verification_request_send(int step, struct raop_session *rs, void (*cb)(struct evrtsp_request *, void *))
raop_pair_request_send(int step, struct raop_session *rs, void (*cb)(struct evrtsp_request *, void *))
{
struct evrtsp_request *req;
uint8_t *body;
uint32_t len;
size_t len;
const char *errmsg;
const char *url;
const char *ctype;
@ -4059,32 +4044,32 @@ raop_verification_request_send(int step, struct raop_session *rs, void (*cb)(str
switch (step)
{
case 1:
body = verification_setup_request1(&len, rs->verification_setup_ctx);
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
body = pair_setup_request1(&len, rs->pair_setup_ctx);
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
url = "/pair-setup-pin";
ctype = "application/x-apple-binary-plist";
break;
case 2:
body = verification_setup_request2(&len, rs->verification_setup_ctx);
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
body = pair_setup_request2(&len, rs->pair_setup_ctx);
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
url = "/pair-setup-pin";
ctype = "application/x-apple-binary-plist";
break;
case 3:
body = verification_setup_request3(&len, rs->verification_setup_ctx);
errmsg = verification_setup_errmsg(rs->verification_setup_ctx);
body = pair_setup_request3(&len, rs->pair_setup_ctx);
errmsg = pair_setup_errmsg(rs->pair_setup_ctx);
url = "/pair-setup-pin";
ctype = "application/x-apple-binary-plist";
break;
case 4:
body = verification_verify_request1(&len, rs->verification_verify_ctx);
errmsg = verification_verify_errmsg(rs->verification_verify_ctx);
body = pair_verify_request1(&len, rs->pair_verify_ctx);
errmsg = pair_verify_errmsg(rs->pair_verify_ctx);
url = "/pair-verify";
ctype = "application/octet-stream";
break;
case 5:
body = verification_verify_request2(&len, rs->verification_verify_ctx);
errmsg = verification_verify_errmsg(rs->verification_verify_ctx);
body = pair_verify_request2(&len, rs->pair_verify_ctx);
errmsg = pair_verify_errmsg(rs->pair_verify_ctx);
url = "/pair-verify";
ctype = "application/octet-stream";
break;
@ -4135,15 +4120,15 @@ raop_verification_request_send(int step, struct raop_session *rs, void (*cb)(str
}
static void
raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg)
raop_cb_pair_verify_step2(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
struct output_device *device;
int ret;
verification_verify_free(rs->verification_verify_ctx);
pair_verify_free(rs->pair_verify_ctx);
ret = raop_verification_response_process(5, req, rs);
ret = raop_pair_response_process(5, req, rs);
if (ret < 0)
{
device = outputs_device_get(rs->device_id);
@ -4170,13 +4155,13 @@ raop_cb_verification_verify_step2(struct evrtsp_request *req, void *arg)
}
static void
raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg)
raop_cb_pair_verify_step1(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
struct output_device *device;
int ret;
ret = raop_verification_response_process(4, req, rs);
ret = raop_pair_response_process(4, req, rs);
if (ret < 0)
{
device = outputs_device_get(rs->device_id);
@ -4189,22 +4174,22 @@ raop_cb_verification_verify_step1(struct evrtsp_request *req, void *arg)
goto error;
}
ret = raop_verification_request_send(5, rs, raop_cb_verification_verify_step2);
ret = raop_pair_request_send(5, rs, raop_cb_pair_verify_step2);
if (ret < 0)
goto error;
return;
error:
verification_verify_free(rs->verification_verify_ctx);
rs->verification_verify_ctx = NULL;
pair_verify_free(rs->pair_verify_ctx);
rs->pair_verify_ctx = NULL;
rs->state = RAOP_STATE_PASSWORD;
session_failure(rs);
}
static int
raop_verification_verify(struct raop_session *rs)
raop_pair_verify(struct raop_session *rs)
{
struct output_device *device;
int ret;
@ -4213,37 +4198,36 @@ raop_verification_verify(struct raop_session *rs)
if (!device)
goto error;
CHECK_NULL(L_RAOP, rs->verification_verify_ctx = verification_verify_new(device->auth_key));
CHECK_NULL(L_RAOP, rs->pair_verify_ctx = pair_verify_new(PAIR_FRUIT, device->auth_key, NULL));
ret = raop_verification_request_send(4, rs, raop_cb_verification_verify_step1);
ret = raop_pair_request_send(4, rs, raop_cb_pair_verify_step1);
if (ret < 0)
goto error;
return 0;
error:
verification_verify_free(rs->verification_verify_ctx);
rs->verification_verify_ctx = NULL;
pair_verify_free(rs->pair_verify_ctx);
rs->pair_verify_ctx = NULL;
return -1;
}
static void
raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
raop_cb_pair_setup_step3(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
struct output_device *device;
const char *authorization_key;
int ret;
ret = raop_verification_response_process(3, req, rs);
ret = raop_pair_response_process(3, req, rs);
if (ret < 0)
goto out;
ret = verification_setup_result(&authorization_key, rs->verification_setup_ctx);
ret = pair_setup_result(&authorization_key, NULL, NULL, rs->pair_setup_ctx);
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Verification setup result error: %s\n", verification_setup_errmsg(rs->verification_setup_ctx));
DPRINTF(E_LOG, L_RAOP, "Verification setup result error: %s\n", pair_setup_errmsg(rs->pair_setup_ctx));
goto out;
}
@ -4263,8 +4247,8 @@ raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
rs->state = RAOP_STATE_STOPPED;
out:
verification_setup_free(rs->verification_setup_ctx);
rs->verification_setup_ctx = NULL;
pair_setup_free(rs->pair_setup_ctx);
rs->pair_setup_ctx = NULL;
// Callback to player with result
raop_status(rs);
@ -4275,62 +4259,62 @@ raop_cb_verification_setup_step3(struct evrtsp_request *req, void *arg)
}
static void
raop_cb_verification_setup_step2(struct evrtsp_request *req, void *arg)
raop_cb_pair_setup_step2(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
int ret;
ret = raop_verification_response_process(2, req, rs);
ret = raop_pair_response_process(2, req, rs);
if (ret < 0)
goto error;
ret = raop_verification_request_send(3, rs, raop_cb_verification_setup_step3);
ret = raop_pair_request_send(3, rs, raop_cb_pair_setup_step3);
if (ret < 0)
goto error;
return;
error:
verification_setup_free(rs->verification_setup_ctx);
rs->verification_setup_ctx = NULL;
pair_setup_free(rs->pair_setup_ctx);
rs->pair_setup_ctx = NULL;
session_failure(rs);
}
static void
raop_cb_verification_setup_step1(struct evrtsp_request *req, void *arg)
raop_cb_pair_setup_step1(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
int ret;
ret = raop_verification_response_process(1, req, rs);
ret = raop_pair_response_process(1, req, rs);
if (ret < 0)
goto error;
ret = raop_verification_request_send(2, rs, raop_cb_verification_setup_step2);
ret = raop_pair_request_send(2, rs, raop_cb_pair_setup_step2);
if (ret < 0)
goto error;
return;
error:
verification_setup_free(rs->verification_setup_ctx);
rs->verification_setup_ctx = NULL;
pair_setup_free(rs->pair_setup_ctx);
rs->pair_setup_ctx = NULL;
session_failure(rs);
}
static int
raop_verification_setup(struct raop_session *rs, const char *pin)
raop_pair_setup(struct raop_session *rs, const char *pin)
{
int ret;
rs->verification_setup_ctx = verification_setup_new(pin);
if (!rs->verification_setup_ctx)
rs->pair_setup_ctx = pair_setup_new(PAIR_FRUIT, pin, NULL);
if (!rs->pair_setup_ctx)
{
DPRINTF(E_LOG, L_RAOP, "Out of memory for verification setup context\n");
return -1;
}
ret = raop_verification_request_send(1, rs, raop_cb_verification_setup_step1);
ret = raop_pair_request_send(1, rs, raop_cb_pair_setup_step1);
if (ret < 0)
goto error;
@ -4339,8 +4323,8 @@ raop_verification_setup(struct raop_session *rs, const char *pin)
return 0;
error:
verification_setup_free(rs->verification_setup_ctx);
rs->verification_setup_ctx = NULL;
pair_setup_free(rs->pair_setup_ctx);
rs->pair_setup_ctx = NULL;
return -1;
}
@ -4355,7 +4339,7 @@ raop_device_authorize(struct output_device *device, const char *pin, int callbac
if (!rs)
return -1;
ret = raop_verification_setup(rs, pin);
ret = raop_pair_setup(rs, pin);
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "Could not send verification setup request to '%s' (address %s)\n", device->name, rs->address);
@ -4366,16 +4350,6 @@ raop_device_authorize(struct output_device *device, const char *pin, int callbac
return 1;
}
#else
static int
raop_verification_verify(struct raop_session *rs)
{
DPRINTF(E_LOG, L_RAOP, "Device '%s' requires verification, but forked-daapd was built with --disable-verification\n", rs->devname);
return -1;
}
#endif /* RAOP_VERIFICATION */
/* ------------------ RAOP devices discovery - mDNS callback ---------------- */
/* Thread: main (mdns) */
@ -4702,7 +4676,7 @@ raop_device_start_generic(struct output_device *device, int callback_id, bool on
return -1;
if (device->auth_key)
ret = raop_verification_verify(rs);
ret = raop_pair_verify(rs);
else if (device->requires_auth)
ret = raop_send_req_pin_start(rs, raop_cb_pin_start, "device_start");
else
@ -4978,9 +4952,13 @@ raop_deinit(void)
struct output_definition output_raop =
{
.name = "AirPlay",
.name = "AirPlay 1",
.type = OUTPUT_TYPE_RAOP,
#ifdef PREFER_AIRPLAY2
.priority = 2,
#else
.priority = 1,
#endif
.disabled = 0,
.init = raop_init,
.deinit = raop_deinit,
@ -4996,7 +4974,5 @@ struct output_definition output_raop =
.metadata_prepare = raop_metadata_prepare,
.metadata_send = raop_metadata_send,
.metadata_purge = raop_metadata_purge,
#ifdef RAOP_VERIFICATION
.device_authorize = raop_device_authorize,
#endif
};

View File

@ -1,66 +0,0 @@
#ifndef __VERIFICATION_H__
#define __VERIFICATION_H__
#include <stdint.h>
struct verification_setup_context;
struct verification_verify_context;
/* When you have the pin-code (must be 4 bytes), create a new context with this
* function and then call verification_setup_request1()
*/
struct verification_setup_context *
verification_setup_new(const char *pin);
void
verification_setup_free(struct verification_setup_context *sctx);
/* Returns last error message
*/
const char *
verification_setup_errmsg(struct verification_setup_context *sctx);
uint8_t *
verification_setup_request1(uint32_t *len, struct verification_setup_context *sctx);
uint8_t *
verification_setup_request2(uint32_t *len, struct verification_setup_context *sctx);
uint8_t *
verification_setup_request3(uint32_t *len, struct verification_setup_context *sctx);
int
verification_setup_response1(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
int
verification_setup_response2(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
int
verification_setup_response3(struct verification_setup_context *sctx, const uint8_t *data, uint32_t data_len);
/* Returns a 0-terminated string that is the authorisation key. The caller
* should save it and use it later to initialize verification_verify_new().
* Note that the pointer becomes invalid when you free sctx.
*/
int
verification_setup_result(const char **authorisation_key, struct verification_setup_context *sctx);
/* When you have completed the setup you can extract a key with
* verification_setup_result(). Give the string as input to this function to
* create a verification context and then call verification_verify_request1()
*/
struct verification_verify_context *
verification_verify_new(const char *authorisation_key);
void
verification_verify_free(struct verification_verify_context *vctx);
/* Returns last error message
*/
const char *
verification_verify_errmsg(struct verification_verify_context *vctx);
uint8_t *
verification_verify_request1(uint32_t *len, struct verification_verify_context *vctx);
uint8_t *
verification_verify_request2(uint32_t *len, struct verification_verify_context *vctx);
int
verification_verify_response1(struct verification_verify_context *vctx, const uint8_t *data, uint32_t data_len);
#endif /* !__VERIFICATION_H__ */

View File

@ -136,6 +136,7 @@ rtp_packet_next(struct rtp_session *session, size_t payload_len, int samples, ch
}
pkt->samples = samples;
pkt->header_len = RTP_HEADER_LEN;
pkt->payload_len = payload_len;
pkt->data_len = RTP_HEADER_LEN + payload_len;
pkt->seqnum = session->seqnum;

View File

@ -35,6 +35,7 @@ struct rtp_packet
int samples; // Number of samples in the packet
uint8_t *header; // Pointer to the RTP header
size_t header_len; // Length of RTP header (12 bytes)
uint8_t *payload; // Pointer to the RTP payload
size_t payload_size; // Size of allocated memory for RTP payload