mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
Merge branch 'airplay2_5'
This commit is contained in:
commit
541e022a0e
21
INSTALL.md
21
INSTALL.md
@ -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,14 +260,14 @@ Libraries:
|
||||
from <https://github.com/json-c/json-c/wiki>
|
||||
- libcurl
|
||||
from <http://curl.haxx.se/libcurl/>
|
||||
- libplist 0.16+
|
||||
from <http://github.com/JonathanBeck/libplist/downloads>
|
||||
- libsodium
|
||||
from <https://download.libsodium.org/doc/>
|
||||
- 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)
|
||||
from <http://github.com/JonathanBeck/libplist/downloads>
|
||||
- libsodium (optional - Apple TV device verification)
|
||||
from <https://download.libsodium.org/doc/>
|
||||
- libspotify (optional - Spotify support)
|
||||
from <https://developer.spotify.com>
|
||||
- libgnutls (optional - Chromecast support)
|
||||
@ -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.
|
||||
|
||||
|
45
configure.ac
45
configure.ac
@ -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"]],
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -47,6 +47,7 @@ extern "C" {
|
||||
#define RTSP_OK 200
|
||||
#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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
117
src/logger.c
117
src/logger.c
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
54
src/misc.c
54
src/misc.c
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
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
240
src/outputs/pair-internal.h
Normal 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
578
src/outputs/pair.c
Normal 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
117
src/outputs/pair.h
Normal 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__ */
|
@ -1,10 +1,9 @@
|
||||
/*
|
||||
*
|
||||
* 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
|
||||
@ -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,17 +60,6 @@ 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;
|
||||
@ -219,8 +72,8 @@ struct SRPUser
|
||||
const unsigned char *bytes_A;
|
||||
int authenticated;
|
||||
|
||||
const char *username;
|
||||
const unsigned char *password;
|
||||
char *username;
|
||||
unsigned char *password;
|
||||
int password_len;
|
||||
|
||||
unsigned char M [SHA512_DIGEST_LENGTH];
|
||||
@ -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)
|
||||
{
|
||||
@ -457,26 +149,6 @@ calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsi
|
||||
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)
|
||||
{
|
||||
@ -514,7 +186,7 @@ calculate_M(enum hash_alg alg, NGConstant *ng, unsigned char *dest, const char *
|
||||
|
||||
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 );
|
||||
@ -564,15 +236,15 @@ srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
||||
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;
|
||||
@ -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;
|
||||
@ -610,13 +282,11 @@ srp_user_delete(struct SRPUser *usr)
|
||||
|
||||
free_ng(usr->ng);
|
||||
|
||||
memset((void*)usr->password, 0, usr->password_len);
|
||||
memset(usr->password, 0, usr->password_len);
|
||||
|
||||
free((char *)usr->username);
|
||||
free((char *)usr->password);
|
||||
|
||||
if (usr->bytes_A)
|
||||
free( (char *)usr->bytes_A );
|
||||
free(usr->username);
|
||||
free(usr->password);
|
||||
free((char *)usr->bytes_A);
|
||||
|
||||
memset(usr, 0, sizeof(*usr));
|
||||
free(usr);
|
||||
@ -709,11 +379,13 @@ srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, in
|
||||
calculate_H_AMK(usr->alg, usr->H_AMK, usr->A, usr->M, usr->session_key, usr->session_key_len);
|
||||
|
||||
*bytes_M = usr->M;
|
||||
if (len_M)
|
||||
*len_M = hash_length(usr->alg);
|
||||
}
|
||||
else
|
||||
{
|
||||
*bytes_M = NULL;
|
||||
if (len_M)
|
||||
*len_M = 0;
|
||||
}
|
||||
|
||||
@ -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
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
66
src/outputs/plist_wrap.h
Normal 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;
|
||||
}
|
@ -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
|
||||
};
|
||||
|
@ -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__ */
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user