Use pair_ap for pairing and rtsp encryption

This commit is contained in:
ejurgensen
2021-05-04 20:34:37 +02:00
parent 073e551ce8
commit 493e9604d3
8 changed files with 141 additions and 1917 deletions
+2 -4
View File
@@ -2,7 +2,6 @@ SUBDIRS = man
ARFLAGS = cr
lib_pair_ap_a_CFLAGS = -Wall -g -DCONFIG_OPENSSL -pthread
lib_csrp_a_CFLAGS = -pthread
lib_tinyhttp_a_CFLAGS = -pthread
lib_dbus_interface_a_CFLAGS = -pthread
lib_mpris_interface_a_CFLAGS = -pthread
@@ -116,10 +115,9 @@ endif
if USE_AIRPLAY_2
shairport_sync_SOURCES += ptp-utilities.c plist_xml_strings.c
shairport_sync_LDADD += lib_csrp.a lib_pair_ap.a
shairport_sync_LDADD += lib_pair_ap.a
lib_pair_ap_a_SOURCES = pair_ap/pair.c pair_ap/pair_fruit.c pair_ap/pair_homekit.c pair_ap/pair-tlv.c
lib_csrp_a_SOURCES = csrp/srp.c
noinst_LIBRARIES += lib_pair_ap.a lib_csrp.a
noinst_LIBRARIES += lib_pair_ap.a
plist_xml_strings.h: plists/get_info_response.xml
echo "// Do not edit!" > plist_xml_strings.h
echo "// This file is automatically generated from files in the plists folder." >> plist_xml_strings.h
-21
View File
@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Tom Cocagne
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.
-155
View File
@@ -1,155 +0,0 @@
csrp
====
Tom Cocagne <tom.cocagne@gmail.com>
csrp is a minimal C implementation of the [Secure Remote Password
protocol](http://srp.stanford.edu/). The project consists of a single
C file and is intended for direct inclusion into utilizing programs.
It's only dependency is OpenSSL.
*NOTE* This SRP implementation was created before the hashing algoritim specified
in RFC 5054 became the de-facto standard for interoperable SRP implementations.
The rfc5054_compat branch of this repository uses the RFC 5054 hashing algorithms
and is known to be compatible with other SRP implementations. If this version works
for you, please consider submitting a patch to this library that implements both the
original default and the RFC 5054 implementation to allow a single mainline version
of this library going forward.
SRP Overview
------------
SRP is a cryptographically strong authentication
protocol for password-based, mutual authentication over an insecure
network connection.
Unlike other common challenge-response autentication protocols, such
as Kerberos and SSL, SRP does not rely on an external infrastructure
of trusted key servers or certificate management. Instead, SRP server
applications use verification keys derived from each user's password
to determine the authenticity of a network connection.
SRP provides mutual-authentication in that successful authentication
requires both sides of the connection to have knowledge of the
user's password. If the client side lacks the user's password or the
server side lacks the proper verification key, the authentication will
fail.
Unlike SSL, SRP does not directly encrypt all data flowing through
the authenticated connection. However, successful authentication does
result in a cryptographically strong shared key that can be used
for symmetric-key encryption.
This library serves as the basis for a compatible Python module called
[pysrp](https://github.com/cocagne/pysrp). The
[pysrp](https://github.com/cocagne/pysrp) project contains complete,
user-friendly API documentation as well as a comprehensive overview of the SRP
protocol. As the APIs are virtually identical, the [pysrp
documentation](http://pythonhosted.org/srp/) is an excellent reference for
understanding this library.
Usage Example
-------------
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "srp.h"
int main( int argc, char * argv[] )
{
int auth_failed = 1;
struct SRPVerifier * ver;
struct SRPUser * usr;
const unsigned char * bytes_s = 0;
const unsigned char * bytes_v = 0;
const unsigned char * bytes_A = 0;
const unsigned char * bytes_B = 0;
const unsigned char * bytes_M = 0;
const unsigned char * bytes_HAMK = 0;
int len_s = 0;
int len_v = 0;
int len_A = 0;
int len_B = 0;
int len_M = 0;
const char * username = "testuser";
const char * password = "password";
const char * auth_username = 0;
SRP_HashAlgorithm alg = SRP_SHA1;
SRP_NGType ng_type = SRP_NG_2048;
/* Create a salt+verification key for the user's password. The salt and
* key need to be computed at the time the user's password is set and
* must be stored by the server-side application for use during the
* authentication process.
*/
srp_create_salted_verification_key( alg, ng_type, username,
(const unsigned char *)password,
strlen(password),
&bytes_s, &len_s,
&bytes_v, &len_v,
NULL, NULL );
/* Begin authentication process */
usr = srp_user_new( alg, ng_type, username,
(const unsigned char *)password,
strlen(password), NULL, NULL );
srp_user_start_authentication( usr, &auth_username, &bytes_A, &len_A );
/* User -> Host: (username, bytes_A) */
ver = srp_verifier_new( alg, ng_type, username, bytes_s, len_s, bytes_v, len_v,
bytes_A, len_A, & bytes_B, &len_B, NULL, NULL );
if ( !bytes_B ) {
printf("Verifier SRP-6a safety check violated!\n");
goto auth_failed;
}
/* Host -> User: (bytes_s, bytes_B) */
srp_user_process_challenge( usr, bytes_s, len_s, bytes_B, len_B, &bytes_M, &len_M );
if ( !bytes_M ) {
printf("User SRP-6a safety check violation!\n");
goto auth_failed;
}
/* User -> Host: (bytes_M) */
srp_verifier_verify_session( ver, bytes_M, &bytes_HAMK );
if ( !bytes_HAMK ) {
printf("User authentication failed!\n");
goto auth_failed;
}
/* Host -> User: (HAMK) */
srp_user_verify_session( usr, bytes_HAMK );
if ( !srp_user_is_authenticated(usr) ) {
printf("Server authentication failed!\n");
goto auth_failed;
}
auth_failed = 0; /* auth success! */
auth_failed:
srp_verifier_delete( ver );
srp_user_delete( usr );
free( (char *)bytes_s );
free( (char *)bytes_v );
return auth_failed;
}
```
-955
View File
@@ -1,955 +0,0 @@
/*
* Secure Remote Password 6a implementation
* Copyright (c) 2010 Tom Cocagne. All rights reserved.
* https://github.com/cocagne/csrp
*
* The MIT License (MIT)
*
* Copyright (c) 2013 Tom Cocagne
*
* 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.
*
*/
#ifdef WIN32
#include <Wincrypt.h>
#else
#include <sys/time.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include "srp.h"
static int g_initialized = 0;
typedef struct
{
BIGNUM * N;
BIGNUM * g;
} NGConstant;
struct NGHex
{
const char * n_hex;
const char * g_hex;
};
/* All constants here were pulled from Appendix A of RFC 5054 */
static struct NGHex global_Ng_constants[] = {
{ /* 1024 */
"EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496"
"EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E"
"F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA"
"9AFD5138FE8376435B9FC61D2FC0EB06E3",
"2"
},
{ /* 2048 */
"AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4"
"A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60"
"95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF"
"747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907"
"8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861"
"60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB"
"FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
"2"
},
{ /* 4096 */
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
"FFFFFFFFFFFFFFFF",
"5"
},
{ /* 8192 */
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
"AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
"DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
"2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
"F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
"BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
"B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
"387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
"6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
"3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
"5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
"2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
"6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
"0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
"359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
"FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
"60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
"13"
},
{0,0} /* null sentinel */
};
static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex )
{
NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) );
ng->N = BN_new();
ng->g = BN_new();
if( !ng || !ng->N || !ng->g )
return 0;
if ( ng_type != SRP_NG_CUSTOM )
{
n_hex = global_Ng_constants[ ng_type ].n_hex;
g_hex = global_Ng_constants[ ng_type ].g_hex;
}
BN_hex2bn( &ng->N, n_hex );
BN_hex2bn( &ng->g, g_hex );
return ng;
}
static void delete_ng( NGConstant * ng )
{
if (ng)
{
BN_free( ng->N );
BN_free( ng->g );
ng->N = 0;
ng->g = 0;
free(ng);
}
}
typedef union
{
SHA_CTX sha;
SHA256_CTX sha256;
SHA512_CTX sha512;
} HashCTX;
struct SRPVerifier
{
SRP_HashAlgorithm hash_alg;
NGConstant *ng;
const char * username;
const unsigned char * bytes_B;
int authenticated;
unsigned char M [SHA512_DIGEST_LENGTH];
unsigned char H_AMK [SHA512_DIGEST_LENGTH];
unsigned char session_key [SHA512_DIGEST_LENGTH];
};
struct SRPUser
{
SRP_HashAlgorithm hash_alg;
NGConstant *ng;
BIGNUM *a;
BIGNUM *A;
BIGNUM *S;
const unsigned char * bytes_A;
int authenticated;
const char * username;
const unsigned char * password;
int password_len;
unsigned char M [SHA512_DIGEST_LENGTH];
unsigned char H_AMK [SHA512_DIGEST_LENGTH];
unsigned char session_key [SHA512_DIGEST_LENGTH];
};
static int hash_init( SRP_HashAlgorithm alg, HashCTX *c )
{
switch (alg)
{
case SRP_SHA1 : return SHA1_Init( &c->sha );
case SRP_SHA224: return SHA224_Init( &c->sha256 );
case SRP_SHA256: return SHA256_Init( &c->sha256 );
case SRP_SHA384: return SHA384_Init( &c->sha512 );
case SRP_SHA512: return SHA512_Init( &c->sha512 );
default:
return -1;
};
}
static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, size_t len )
{
switch (alg)
{
case SRP_SHA1 : return SHA1_Update( &c->sha, data, len );
case SRP_SHA224: return SHA224_Update( &c->sha256, data, len );
case SRP_SHA256: return SHA256_Update( &c->sha256, data, len );
case SRP_SHA384: return SHA384_Update( &c->sha512, data, len );
case SRP_SHA512: return SHA512_Update( &c->sha512, data, len );
default:
return -1;
};
}
static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md )
{
switch (alg)
{
case SRP_SHA1 : return SHA1_Final( md, &c->sha );
case SRP_SHA224: return SHA224_Final( md, &c->sha256 );
case SRP_SHA256: return SHA256_Final( md, &c->sha256 );
case SRP_SHA384: return SHA384_Final( md, &c->sha512 );
case SRP_SHA512: return SHA512_Final( md, &c->sha512 );
default:
return -1;
};
}
static unsigned char * hash( SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md )
{
switch (alg)
{
case SRP_SHA1 : return SHA1( d, n, md );
case SRP_SHA224: return SHA224( d, n, md );
case SRP_SHA256: return SHA256( d, n, md );
case SRP_SHA384: return SHA384( d, n, md );
case SRP_SHA512: return SHA512( d, n, md );
default:
return 0;
};
}
static int hash_length( SRP_HashAlgorithm alg )
{
switch (alg)
{
case SRP_SHA1 : return SHA_DIGEST_LENGTH;
case SRP_SHA224: return SHA224_DIGEST_LENGTH;
case SRP_SHA256: return SHA256_DIGEST_LENGTH;
case SRP_SHA384: return SHA384_DIGEST_LENGTH;
case SRP_SHA512: return SHA512_DIGEST_LENGTH;
default:
return -1;
};
}
static BIGNUM * H_nn( SRP_HashAlgorithm alg, const BIGNUM * n1, const BIGNUM * n2 )
{
unsigned char buff[ SHA512_DIGEST_LENGTH ];
int len_n1 = BN_num_bytes(n1);
int len_n2 = BN_num_bytes(n2);
int nbytes = len_n1 + len_n2;
unsigned char * bin = (unsigned char *) malloc( nbytes );
if (!bin)
return 0;
BN_bn2bin(n1, bin);
BN_bn2bin(n2, bin + len_n1);
hash( alg, bin, nbytes, buff );
free(bin);
return BN_bin2bn(buff, hash_length(alg), NULL);
}
static BIGNUM * H_nn_pad( SRP_HashAlgorithm alg, const BIGNUM * n1, const BIGNUM * n2 )
{
unsigned char * bin;
unsigned char buff[ SHA512_DIGEST_LENGTH ];
int len_n1 = BN_num_bytes(n1);
int len_n2 = BN_num_bytes(n2);
int nbytes = 2 * len_n1;
if ((len_n2 < 1) || (len_n2 > len_n1))
return 0;
bin = (unsigned char *) calloc( 1, nbytes );
if (!bin)
return 0;
BN_bn2bin(n1, bin);
BN_bn2bin(n2, bin + nbytes - len_n2);
hash( alg, bin, nbytes, buff );
free(bin);
return BN_bin2bn(buff, hash_length(alg), NULL);
}
static BIGNUM * H_ns( SRP_HashAlgorithm alg, const BIGNUM * n, const unsigned char * bytes, int len_bytes )
{
unsigned char buff[ SHA512_DIGEST_LENGTH ];
int len_n = BN_num_bytes(n);
int nbytes = len_n + len_bytes;
unsigned char * bin = (unsigned char *) malloc( nbytes );
if (!bin)
return 0;
BN_bn2bin(n, bin);
memcpy( bin + len_n, bytes, len_bytes );
hash( alg, bin, nbytes, buff );
free(bin);
return BN_bin2bn(buff, hash_length(alg), NULL);
}
static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const char * username, const unsigned char * password, int password_len )
{
unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
HashCTX ctx;
hash_init( alg, &ctx );
hash_update( alg, &ctx, username, strlen(username) );
hash_update( alg, &ctx, ":", 1 );
hash_update( alg, &ctx, password, password_len );
hash_final( alg, &ctx, ucp_hash );
return H_ns( alg, salt, ucp_hash, hash_length(alg) );
}
static void update_hash_n( SRP_HashAlgorithm alg, HashCTX *ctx, const BIGNUM * n )
{
unsigned long len = BN_num_bytes(n);
unsigned char * n_bytes = (unsigned char *) malloc( len );
if (!n_bytes)
return;
BN_bn2bin(n, n_bytes);
hash_update(alg, ctx, n_bytes, len);
free(n_bytes);
}
static void hash_num( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest )
{
int nbytes = BN_num_bytes(n);
unsigned char * bin = (unsigned char *) malloc( nbytes );
if(!bin)
return;
BN_bn2bin(n, bin);
hash( alg, bin, nbytes, dest );
free(bin);
}
static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const BIGNUM * s,
const BIGNUM * A, const BIGNUM * B, const unsigned char * K )
{
unsigned char H_N[ SHA512_DIGEST_LENGTH ];
unsigned char H_g[ SHA512_DIGEST_LENGTH ];
unsigned char H_I[ SHA512_DIGEST_LENGTH ];
unsigned char H_xor[ SHA512_DIGEST_LENGTH ];
HashCTX ctx;
int i = 0;
int hash_len = hash_length(alg);
hash_num( alg, ng->N, H_N );
hash_num( alg, ng->g, H_g );
hash(alg, (const unsigned char *)I, strlen(I), H_I);
for (i=0; i < hash_len; i++ )
H_xor[i] = H_N[i] ^ H_g[i];
hash_init( alg, &ctx );
hash_update( alg, &ctx, H_xor, hash_len );
hash_update( alg, &ctx, H_I, hash_len );
update_hash_n( alg, &ctx, s );
update_hash_n( alg, &ctx, A );
update_hash_n( alg, &ctx, B );
hash_update( alg, &ctx, K, hash_len );
hash_final( alg, &ctx, dest );
}
static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const BIGNUM * A, const unsigned char * M, const unsigned char * K )
{
HashCTX ctx;
hash_init( alg, &ctx );
update_hash_n( alg, &ctx, A );
hash_update( alg, &ctx, M, hash_length(alg) );
hash_update( alg, &ctx, K, hash_length(alg) );
hash_final( alg, &ctx, dest );
}
static void init_random()
{
#ifdef WIN32
HCRYPTPROV wctx;
#else
FILE *fp = 0;
#endif
unsigned char buff[64];
if (g_initialized)
return;
#ifdef WIN32
CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptGenRandom(wctx, sizeof(buff), (BYTE*) buff);
CryptReleaseContext(wctx, 0);
g_initialized = 1;
#else
fp = fopen("/dev/urandom", "r");
if (fp)
{
size_t read = fread(buff, sizeof(buff), 1, fp);
g_initialized = read == 1;
fclose(fp);
}
#endif
if (g_initialized)
RAND_seed( buff, sizeof(buff) );
}
/***********************************************************************************************************
*
* Exported Functions
*
***********************************************************************************************************/
void srp_random_seed( const unsigned char * random_data, int data_length )
{
g_initialized = 1;
if (random_data)
RAND_seed( random_data, data_length );
}
void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
SRP_NGType ng_type, const char * username,
const unsigned char * password, int len_password,
const unsigned char ** bytes_s, int * len_s,
const unsigned char ** bytes_v, int * len_v,
const char * n_hex, const char * g_hex )
{
BIGNUM * s = BN_new();
BIGNUM * v = BN_new();
BIGNUM * x = 0;
BN_CTX * ctx = BN_CTX_new();
NGConstant * ng = new_ng( ng_type, n_hex, g_hex );
if( !s || !v || !ctx || !ng )
goto cleanup_and_exit;
init_random(); /* Only happens once */
BN_rand(s, 4*32, -1, 0); // MODIFIED!!!
x = calculate_x( alg, s, username, password, len_password );
if( !x )
goto cleanup_and_exit;
BN_mod_exp(v, ng->g, x, ng->N, ctx);
*len_s = BN_num_bytes(s);
*len_v = BN_num_bytes(v);
*bytes_s = (const unsigned char *) malloc( *len_s );
*bytes_v = (const unsigned char *) malloc( *len_v );
if (!bytes_s || !bytes_v)
goto cleanup_and_exit;
BN_bn2bin(s, (unsigned char *) *bytes_s);
BN_bn2bin(v, (unsigned char *) *bytes_v);
cleanup_and_exit:
delete_ng( ng );
BN_free(s);
BN_free(v);
BN_free(x);
BN_CTX_free(ctx);
}
void
srp_verifier_get_B(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const unsigned char * bytes_v, int len_v,
const unsigned char ** bytes_b, int * len_b,
const unsigned char ** bytes_B, int * len_B,
const char * n_hex, const char * g_hex)
{
BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL);
BIGNUM *k = 0;
BIGNUM *b = BN_new();
BIGNUM *B = BN_new();
BIGNUM *tmp1 = BN_new();
BIGNUM *tmp2 = BN_new();
BN_CTX *ctx = BN_CTX_new();
NGConstant *ng = new_ng( ng_type, n_hex, g_hex );
*len_b = 0;
*bytes_b = 0;
*len_B = 0;
*bytes_B = 0;
init_random(); /* Only happens once */
BN_rand(b, 256, -1, 0);
k = H_nn_pad(alg, ng->N, ng->g); //MODIFIED
/* B = kv + g^b */
BN_mul(tmp1, k, v, ctx);
BN_mod_exp(tmp2, ng->g, b, ng->N, ctx);
BN_mod_add(B, tmp1, tmp2, ng->N, ctx);
*len_B = BN_num_bytes(B);
*bytes_B = (const unsigned char *) malloc( *len_B );
BN_bn2bin( B, (unsigned char *) *bytes_B );
*len_b = BN_num_bytes(b);
*bytes_b = (const unsigned char *) malloc( *len_b );
BN_bn2bin( b, (unsigned char *) *bytes_b );
BN_free(b); // Added -- not sure about these, but they look necessary
BN_free(B); // Added -- not sure about these, but they look necessary
BN_free(v);
BN_free(k);
BN_free(tmp1);
BN_free(tmp2);
BN_CTX_free(ctx);
delete_ng(ng);
}
/* Out: bytes_B, len_B.
*
* On failure, bytes_B will be set to NULL and len_B will be set to 0
*/
struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
const unsigned char * bytes_s, int len_s,
const unsigned char * bytes_v, int len_v,
const unsigned char * bytes_A, int len_A,
const unsigned char * bytes_b, int len_b,
const unsigned char * bytes_B, int len_B,
const char * n_hex, const char * g_hex )
{
BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL);
BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL);
BIGNUM *A = BN_bin2bn(bytes_A, len_A, NULL);
BIGNUM *u = 0;
BIGNUM *b = BN_bin2bn(bytes_b, len_b, NULL);;
BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL);;
BIGNUM *S = BN_new();
BIGNUM *k = 0;
BIGNUM *tmp1 = BN_new();
BIGNUM *tmp2 = BN_new();
BN_CTX *ctx = BN_CTX_new();
int ulen = strlen(username) + 1;
NGConstant *ng = new_ng( ng_type, n_hex, g_hex );
struct SRPVerifier *ver = 0;
if( !s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ctx || !ng )
goto cleanup_and_exit;
ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) );
if (!ver)
goto cleanup_and_exit;
init_random(); /* Only happens once */
ver->username = (char *) malloc( ulen );
ver->hash_alg = alg;
ver->ng = ng;
if (!ver->username)
{
free(ver);
ver = 0;
goto cleanup_and_exit;
}
memcpy( (char*)ver->username, username, ulen );
ver->authenticated = 0;
/* SRP-6a safety check */
BN_mod(tmp1, A, ng->N, ctx);
if ( !BN_is_zero(tmp1) )
{
k = H_nn_pad(alg, ng->N, ng->g); //MODIFIED
u = H_nn(alg, A, B);
/* S = (A *(v^u)) ^ b */
BN_mod_exp(tmp1, v, u, ng->N, ctx);
BN_mul(tmp2, A, tmp1, ctx);
BN_mod_exp(S, tmp2, b, ng->N, ctx);
hash_num(alg, S, ver->session_key);
calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key );
calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key );
ver->bytes_B = bytes_B;
}
cleanup_and_exit:
BN_free(s);
BN_free(v);
BN_free(A);
if (u) BN_free(u);
if (k) BN_free(k);
BN_free(B);
BN_free(S);
BN_free(b);
BN_free(tmp1);
BN_free(tmp2);
BN_CTX_free(ctx);
return ver;
}
void srp_verifier_delete( struct SRPVerifier * ver )
{
if (ver)
{
delete_ng( ver->ng );
free( (char *) ver->username );
free( (unsigned char *) ver->bytes_B );
memset(ver, 0, sizeof(*ver));
free( ver );
}
}
int srp_verifier_is_authenticated( struct SRPVerifier * ver )
{
return ver->authenticated;
}
const char * srp_verifier_get_username( struct SRPVerifier * ver )
{
return ver->username;
}
const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length )
{
if (key_length)
*key_length = hash_length( ver->hash_alg );
return ver->session_key;
}
int srp_verifier_get_session_key_length( struct SRPVerifier * ver )
{
return hash_length( ver->hash_alg );
}
/* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */
void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK )
{
if ( memcmp( ver->M, user_M, hash_length(ver->hash_alg) ) == 0 )
{
ver->authenticated = 1;
*bytes_HAMK = ver->H_AMK;
}
else
*bytes_HAMK = NULL;
}
/*******************************************************************************/
struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
const unsigned char * bytes_password, int len_password,
const char * n_hex, const char * g_hex )
{
struct SRPUser *usr = (struct SRPUser *) malloc( sizeof(struct SRPUser) );
int ulen = strlen(username) + 1;
if (!usr)
goto err_exit;
init_random(); /* Only happens once */
usr->hash_alg = alg;
usr->ng = new_ng( ng_type, n_hex, g_hex );
usr->a = BN_new();
usr->A = BN_new();
usr->S = BN_new();
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->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);
usr->authenticated = 0;
usr->bytes_A = 0;
return usr;
err_exit:
if (usr)
{
BN_free(usr->a);
BN_free(usr->A);
BN_free(usr->S);
if (usr->username)
free((void*)usr->username);
if (usr->password)
{
memset((void*)usr->password, 0, usr->password_len);
free((void*)usr->password);
}
free(usr);
}
return 0;
}
void srp_user_delete( struct SRPUser * usr )
{
if( usr )
{
BN_free( usr->a );
BN_free( usr->A );
BN_free( usr->S );
delete_ng( usr->ng );
memset((void*)usr->password, 0, usr->password_len);
free((char *)usr->username);
free((char *)usr->password);
if (usr->bytes_A)
free( (char *)usr->bytes_A );
memset(usr, 0, sizeof(*usr));
free( usr );
}
}
int srp_user_is_authenticated( struct SRPUser * usr)
{
return usr->authenticated;
}
const char * srp_user_get_username( struct SRPUser * usr )
{
return usr->username;
}
const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length )
{
if (key_length)
*key_length = hash_length( usr->hash_alg );
return usr->session_key;
}
int srp_user_get_session_key_length( struct SRPUser * usr )
{
return hash_length( usr->hash_alg );
}
/* Output: username, bytes_A, len_A */
void srp_user_start_authentication( struct SRPUser * usr, const char ** username,
const unsigned char ** bytes_A, int * len_A )
{
BN_CTX *ctx = BN_CTX_new();
BN_rand(usr->a, 256, -1, 0);
BN_mod_exp(usr->A, usr->ng->g, usr->a, usr->ng->N, ctx);
BN_CTX_free(ctx);
*len_A = BN_num_bytes(usr->A);
*bytes_A = (const unsigned char *) malloc( *len_A );
if (!*bytes_A)
{
*len_A = 0;
*bytes_A = 0;
*username = 0;
return;
}
BN_bn2bin( usr->A, (unsigned char *) *bytes_A );
usr->bytes_A = *bytes_A;
*username = usr->username;
}
/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */
void srp_user_process_challenge( struct SRPUser * usr,
const unsigned char * bytes_s, int len_s,
const unsigned char * bytes_B, int len_B,
const unsigned char ** bytes_M, int * len_M )
{
BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL);
BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL);
BIGNUM *u = 0;
BIGNUM *x = 0;
BIGNUM *k = 0;
BIGNUM *v = BN_new();
BIGNUM *tmp1 = BN_new();
BIGNUM *tmp2 = BN_new();
BIGNUM *tmp3 = BN_new();
BN_CTX *ctx = BN_CTX_new();
*len_M = 0;
*bytes_M = 0;
if( !s || !B || !v || !tmp1 || !tmp2 || !tmp3 || !ctx )
goto cleanup_and_exit;
u = H_nn_pad(usr->hash_alg, usr->A, B); //MODIFIED
if (!u)
goto cleanup_and_exit;
x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len );
if (!x)
goto cleanup_and_exit;
k = H_nn_pad(usr->hash_alg, usr->ng->N, usr->ng->g); //MODIFIED
if (!k)
goto cleanup_and_exit;
/* SRP-6a safety check */
if ( !BN_is_zero(B) && !BN_is_zero(u) )
{
BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx);
/* S = (B - k*(g^x)) ^ (a + ux) */
BN_mul(tmp1, u, x, ctx);
BN_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */
BN_mod_exp(tmp1, usr->ng->g, x, usr->ng->N, ctx);
BN_mul(tmp3, k, tmp1, ctx); /* tmp3 = k*(g^x) */
BN_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */
BN_mod_exp(usr->S, tmp1, tmp2, usr->ng->N, ctx);
hash_num(usr->hash_alg, usr->S, usr->session_key);
calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key );
calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key );
*bytes_M = usr->M;
if (len_M)
*len_M = hash_length( usr->hash_alg );
}
else
{
*bytes_M = NULL;
if (len_M)
*len_M = 0;
}
cleanup_and_exit:
BN_free(s);
BN_free(B);
BN_free(u);
BN_free(x);
BN_free(k);
BN_free(v);
BN_free(tmp1);
BN_free(tmp2);
BN_free(tmp3);
BN_CTX_free(ctx);
}
void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK )
{
if ( memcmp( usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg) ) == 0 )
usr->authenticated = 1;
}
-193
View File
@@ -1,193 +0,0 @@
/*
* Secure Remote Password 6a implementation
* Copyright (c) 2010 Tom Cocagne. All rights reserved.
* https://github.com/cocagne/csrp
*
* The MIT License (MIT)
*
* Copyright (c) 2013 Tom Cocagne
*
* 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.
*
*/
/*
*
* Purpose: This is a direct implementation of the Secure Remote Password
* Protocol version 6a as described by
* http://srp.stanford.edu/design.html
*
* Author: tom.cocagne@gmail.com (Tom Cocagne)
*
* Dependencies: OpenSSL (and Advapi32.lib on Windows)
*
* Usage: Refer to test_srp.c for a demonstration
*
* Notes:
* This library allows multiple combinations of hashing algorithms and
* prime number constants. For authentication to succeed, the hash and
* prime number constants must match between
* srp_create_salted_verification_key(), srp_user_new(),
* and srp_verifier_new(). A recommended approach is to determine the
* desired level of security for an application and globally define the
* hash and prime number constants to the predetermined values.
*
* As one might suspect, more bits means more security. As one might also
* suspect, more bits also means more processing time. The test_srp.c
* program can be easily modified to profile various combinations of
* hash & prime number pairings.
*/
#ifndef SRP_H
#define SRP_H
struct SRPVerifier;
struct SRPUser;
typedef enum
{
SRP_NG_1024,
SRP_NG_2048,
SRP_NG_4096,
SRP_NG_8192,
SRP_NG_CUSTOM
} SRP_NGType;
typedef enum
{
SRP_SHA1,
SRP_SHA224,
SRP_SHA256,
SRP_SHA384,
SRP_SHA512
} SRP_HashAlgorithm;
/* This library will automatically seed the OpenSSL random number generator
* using cryptographically sound random data on Windows & Linux. If this is
* undesirable behavior or the host OS does not provide a /dev/urandom file,
* this function may be called to seed the random number generator with
* alternate data.
*
* The random data should include at least as many bits of entropy as the
* largest hash function used by the application. So, for example, if a
* 512-bit hash function is used, the random data requies at least 512
* bits of entropy.
*
* Passing a null pointer to this function will cause this library to skip
* seeding the random number generator. This is only legitimate if it is
* absolutely known that the OpenSSL random number generator has already
* been sufficiently seeded within the running application.
*
* Notes:
* * This function is optional on Windows & Linux and mandatory on all
* other platforms.
*/
void srp_random_seed( const unsigned char * random_data, int data_length );
/* Out: bytes_s, len_s, bytes_v, len_v
*
* The caller is responsible for freeing the memory allocated for bytes_s and bytes_v
*
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type.
* If provided, they must contain ASCII text of the hexidecimal notation.
*/
void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
SRP_NGType ng_type, const char * username,
const unsigned char * password, int len_password,
const unsigned char ** bytes_s, int * len_s,
const unsigned char ** bytes_v, int * len_v,
const char * n_hex, const char * g_hex );
/* Out: bytes_B, len_B.
*/
void
srp_verifier_get_B(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const unsigned char * bytes_v, int len_v,
const unsigned char ** bytes_b, int * len_b,
const unsigned char ** bytes_B, int * len_B,
const char * n_hex, const char * g_hex);
struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
const unsigned char * bytes_s, int len_s,
const unsigned char * bytes_v, int len_v,
const unsigned char * bytes_A, int len_A,
const unsigned char * bytes_b, int len_b,
const unsigned char * bytes_B, int len_B,
const char * n_hex, const char * g_hex );
void srp_verifier_delete( struct SRPVerifier * ver );
int srp_verifier_is_authenticated( struct SRPVerifier * ver );
const char * srp_verifier_get_username( struct SRPVerifier * ver );
/* key_length may be null */
const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length );
int srp_verifier_get_session_key_length( struct SRPVerifier * ver );
/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */
void srp_verifier_verify_session( struct SRPVerifier * ver,
const unsigned char * user_M,
const unsigned char ** bytes_HAMK );
/*******************************************************************************/
/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */
struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username,
const unsigned char * bytes_password, int len_password,
const char * n_hex, const char * g_hex );
void srp_user_delete( struct SRPUser * usr );
int srp_user_is_authenticated( struct SRPUser * usr);
const char * srp_user_get_username( struct SRPUser * usr );
/* key_length may be null */
const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length );
int srp_user_get_session_key_length( struct SRPUser * usr );
/* Output: username, bytes_A, len_A */
void srp_user_start_authentication( struct SRPUser * usr, const char ** username,
const unsigned char ** bytes_A, int * len_A );
/* Output: bytes_M, len_M (len_M may be null and will always be
* srp_user_get_session_key_length() bytes in size) */
void srp_user_process_challenge( struct SRPUser * usr,
const unsigned char * bytes_s, int len_s,
const unsigned char * bytes_B, int len_B,
const unsigned char ** bytes_M, int * len_M );
/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */
void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK );
#endif /* Include Guard */
+1 -1
Submodule pair_ap updated: ad2bc75eda...1b9954cba1
+21 -5
View File
@@ -22,7 +22,7 @@
#endif
#ifdef CONFIG_AIRPLAY_2
#include "pair_ap/pair-internal.h"
#include "pair_ap/pair.h"
#include "plist/plist.h"
#endif
@@ -92,6 +92,23 @@ typedef struct {
typedef enum { ts_ntp, ts_ptp } timing_t;
typedef enum { ap_1, ap_2 } airplay_t;
typedef struct {
uint8_t *data;
size_t len;
size_t size;
} ap2_buffer;
typedef struct {
int is_encrypted;
struct pair_setup_context *setup_ctx;
struct pair_verify_context *verify_ctx;
struct pair_cipher_context *cipher_ctx;
ap2_buffer encrypted_buf;
ap2_buffer plain_buf;
} ap2_pairing;
/*
typedef struct file_cipher_context {
struct pair_cipher_context *cipher_context;
int active; // can be created during a pair setup but not activated until next read
@@ -100,6 +117,7 @@ typedef struct file_cipher_context {
void *input_plaintext_buffer_toq;
size_t input_plaintext_buffer_bytes_occupied;
} file_cipher_context;
*/
#endif
typedef struct {
@@ -248,10 +266,6 @@ typedef struct {
pthread_t rtp_realtime_audio_thread;
pthread_t rtp_buffered_audio_thread;
int pairing_mode;
file_cipher_context control_cipher_context;
struct verifier_setup_context *server_setup_ctx;
int last_anchor_info_is_valid;
uint64_t last_anchor_clock_offset;
uint64_t last_anchor_time_of_update;
@@ -264,6 +278,8 @@ typedef struct {
int ap2_rate; // protect with flush mutex, 0 means don't play, 1 means play
int ap2_play_enabled; // protect with flush mutex
ap2_pairing ap2_control_pairing;
int event_socket;
SOCKADDR ap2_remote_control_socket_addr; // a socket pointing to the control port of the client
socklen_t ap2_remote_control_socket_addr_length;
+117 -583
View File
@@ -85,12 +85,8 @@
#endif
#ifdef CONFIG_AIRPLAY_2
#include "csrp/srp.h"
#include "pair_ap/pair-tlv.h"
#include "pair_ap/pair.h"
#include "plist/plist.h"
#include <sodium.h>
// #include "proxy_ap/server.h"
#include "plist_xml_strings.h"
#include "ptp-utilities.h"
#endif
@@ -803,408 +799,100 @@ void debug_log_rtsp_message(int level, char *prompt, rtsp_message *message) {
}
}
// this will read a block of the size specified to the buffer
// and will return either with the block or on error
ssize_t read_sized_block(int fd, void *buf, size_t count) {
ssize_t response, nread;
size_t inbuf = 0; // bytes already in the buffer
int keep_trying = 1;
#ifdef CONFIG_AIRPLAY_2
static void buf_add(ap2_buffer *buf, uint8_t *in, size_t in_len) {
if (buf->len + in_len > buf->size) {
buf->size = buf->len + in_len + 2048; // Extra legroom to avoid future memcpy's
uint8_t *new = malloc(buf->size);
memcpy(new, buf->data, buf->len);
free(buf->data);
buf->data = new;
}
memcpy(buf->data + buf->len, in, in_len);
buf->len += in_len;
}
static void buf_drain(ap2_buffer *buf, ssize_t len) {
if (len < 0 || (size_t)len >= buf->len) {
free(buf->data);
memset(buf, 0, sizeof(ap2_buffer));
return;
}
memmove(buf->data, buf->data + len, buf->len - len);
buf->len -= len;
}
static size_t buf_remove(ap2_buffer *buf, uint8_t *out, size_t out_len) {
size_t bytes = (buf->len > out_len) ? out_len : buf->len;
memcpy(out, buf->data, bytes);
buf_drain(buf, bytes);
return bytes;
}
static ssize_t read_encrypted(int fd, ap2_pairing *ctx, void *buf, size_t count) {
uint8_t in[4096];
uint8_t *plain;
size_t plain_len;
// If there is leftover decoded content from the last pass just return that
if (ctx->plain_buf.len > 0) {
return buf_remove(&ctx->plain_buf, buf, count);
}
do {
nread = read(fd, buf + inbuf, count - inbuf);
if (nread == 0) {
// a blocking read that returns zero means eof -- implies connection closed
debug(3, "read_sized_block connection closed.");
keep_trying = 0;
} else if (nread < 0) {
if (errno == EAGAIN) {
debug(1, "read_sized_block getting Error 11 -- EAGAIN from a blocking read!");
}
if ((errno != ECONNRESET) && (errno != EAGAIN) && (errno != EINTR)) {
char errorstring[1024];
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
debug(1, "read_sized_block read error %d: \"%s\".", errno, (char *)errorstring);
keep_trying = 0;
}
} else {
inbuf += (size_t)nread;
}
} while ((keep_trying != 0) && (inbuf < count));
if (nread <= 0)
response = nread;
else
response = inbuf;
return response;
}
ssize_t got = read(fd, in, sizeof(in));
if (got <= 0)
return got;
buf_add(&ctx->encrypted_buf, in, got);
#ifdef CONFIG_AIRPLAY_2
ssize_t consumed = pair_decrypt(&plain, &plain_len, ctx->encrypted_buf.data, ctx->encrypted_buf.len, ctx->cipher_ctx);
if (consumed < 0)
return -1;
buf_drain(&ctx->encrypted_buf, consumed);
} while (plain_len == 0);
/* ----------------------------- DEFINES ETC ------------------------------- */
#define USERNAME "Pair-Setup"
#define AUTHTAG_LENGTH 16
#define NONCE_LENGTH 12 // 96 bits according to chacha poly1305
#define RESPONSE_BUFSIZE 8192
#define ENCRYPTED_LEN_MAX 0x400
#define PASSWORD "3939"
#define RTSP_VERSION "RTSP/1.0"
#define POST_PAIR_SETUP "POST /pair-setup"
#define OPTIONS "OPTIONS *"
#define GET_INFO "GET /info"
#define POST_AUTH_SETUP "POST /auth-setup"
#define POST_FP_SETUP "POST /fp-setup"
/* Fairplay magic */
/*
static uint8_t server_fp_reply1[] =
"\x46\x50\x4c\x59\x03\x01\x02\x00\x00\x00\x00\x82\x02\x00\x0f\x9f\x3f\x9e\x0a"
"\x25\x21\xdb\xdf\x31\x2a\xb2\xbf\xb2\x9e\x8d\x23\x2b\x63\x76\xa8\xc8\x18\x70"
"\x1d\x22\xae\x93\xd8\x27\x37\xfe\xaf\x9d\xb4\xfd\xf4\x1c\x2d\xba\x9d\x1f\x49"
"\xca\xaa\xbf\x65\x91\xac\x1f\x7b\xc6\xf7\xe0\x66\x3d\x21\xaf\xe0\x15\x65\x95"
"\x3e\xab\x81\xf4\x18\xce\xed\x09\x5a\xdb\x7c\x3d\x0e\x25\x49\x09\xa7\x98\x31"
"\xd4\x9c\x39\x82\x97\x34\x34\xfa\xcb\x42\xc6\x3a\x1c\xd9\x11\xa6\xfe\x94\x1a"
"\x8a\x6d\x4a\x74\x3b\x46\xc3\xa7\x64\x9e\x44\xc7\x89\x55\xe4\x9d\x81\x55\x00"
"\x95\x49\xc4\xe2\xf7\xa3\xf6\xd5\xba";
static uint8_t server_fp_reply2[] =
"\x46\x50\x4c\x59\x03\x01\x02\x00\x00\x00\x00\x82\x02\x01\xcf\x32\xa2\x57\x14"
"\xb2\x52\x4f\x8a\xa0\xad\x7a\xf1\x64\xe3\x7b\xcf\x44\x24\xe2\x00\x04\x7e\xfc"
"\x0a\xd6\x7a\xfc\xd9\x5d\xed\x1c\x27\x30\xbb\x59\x1b\x96\x2e\xd6\x3a\x9c\x4d"
"\xed\x88\xba\x8f\xc7\x8d\xe6\x4d\x91\xcc\xfd\x5c\x7b\x56\xda\x88\xe3\x1f\x5c"
"\xce\xaf\xc7\x43\x19\x95\xa0\x16\x65\xa5\x4e\x19\x39\xd2\x5b\x94\xdb\x64\xb9"
"\xe4\x5d\x8d\x06\x3e\x1e\x6a\xf0\x7e\x96\x56\x16\x2b\x0e\xfa\x40\x42\x75\xea"
"\x5a\x44\xd9\x59\x1c\x72\x56\xb9\xfb\xe6\x51\x38\x98\xb8\x02\x27\x72\x19\x88"
"\x57\x16\x50\x94\x2a\xd9\x46\x68\x8a";
static uint8_t server_fp_reply3[] =
"\x46\x50\x4c\x59\x03\x01\x02\x00\x00\x00\x00\x82\x02\x02\xc1\x69\xa3\x52\xee"
"\xed\x35\xb1\x8c\xdd\x9c\x58\xd6\x4f\x16\xc1\x51\x9a\x89\xeb\x53\x17\xbd\x0d"
"\x43\x36\xcd\x68\xf6\x38\xff\x9d\x01\x6a\x5b\x52\xb7\xfa\x92\x16\xb2\xb6\x54"
"\x82\xc7\x84\x44\x11\x81\x21\xa2\xc7\xfe\xd8\x3d\xb7\x11\x9e\x91\x82\xaa\xd7"
"\xd1\x8c\x70\x63\xe2\xa4\x57\x55\x59\x10\xaf\x9e\x0e\xfc\x76\x34\x7d\x16\x40"
"\x43\x80\x7f\x58\x1e\xe4\xfb\xe4\x2c\xa9\xde\xdc\x1b\x5e\xb2\xa3\xaa\x3d\x2e"
"\xcd\x59\xe7\xee\xe7\x0b\x36\x29\xf2\x2a\xfd\x16\x1d\x87\x73\x53\xdd\xb9\x9a"
"\xdc\x8e\x07\x00\x6e\x56\xf8\x50\xce";
static uint8_t server_fp_reply4[] =
"\x46\x50\x4c\x59\x03\x01\x02\x00\x00\x00\x00\x82\x02\x03\x90\x01\xe1\x72\x7e"
"\x0f\x57\xf9\xf5\x88\x0d\xb1\x04\xa6\x25\x7a\x23\xf5\xcf\xff\x1a\xbb\xe1\xe9"
"\x30\x45\x25\x1a\xfb\x97\xeb\x9f\xc0\x01\x1e\xbe\x0f\x3a\x81\xdf\x5b\x69\x1d"
"\x76\xac\xb2\xf7\xa5\xc7\x08\xe3\xd3\x28\xf5\x6b\xb3\x9d\xbd\xe5\xf2\x9c\x8a"
"\x17\xf4\x81\x48\x7e\x3a\xe8\x63\xc6\x78\x32\x54\x22\xe6\xf7\x8e\x16\x6d\x18"
"\xaa\x7f\xd6\x36\x25\x8b\xce\x28\x72\x6f\x66\x1f\x73\x88\x93\xce\x44\x31\x1e"
"\x4b\xe6\xc0\x53\x51\x93\xe5\xef\x72\xe8\x68\x62\x33\x72\x9c\x22\x7d\x82\x0c"
"\x99\x94\x45\xd8\x92\x46\xc8\xc3\x59";
static uint8_t server_fp_header[] = "\x46\x50\x4c\x59\x03\x01\x04\x00\x00\x00\x00\x14";
// Fast path, avoids some memcpy + allocs in case of the normal, small message
/* if (ctx->plain_buf.len == 0 && plain_len < count) {
memcpy(buf, plain, plain_len);
free(plain);
return plain_len;
}
*/
buf_add(&ctx->plain_buf, plain, plain_len);
free(plain);
/* 3072 n and g for SRP*/
const char *nl_hex = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B"
"139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485"
"B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1F"
"E649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23"
"DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32"
"905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558"
"17183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521"
"ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D7"
"1E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B1817"
"7B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82"
"D120A93AD2CAFFFFFFFFFFFFFFFF";
const char *gl_hex = "5";
enum pair_keys {
PAIR_SETUP_MSG01 = 0,
PAIR_SETUP_MSG02,
PAIR_SETUP_MSG03,
PAIR_SETUP_MSG04,
PAIR_SETUP_MSG05,
PAIR_SETUP_MSG06,
PAIR_SETUP_SIGN,
PAIR_VERIFY_MSG01,
PAIR_VERIFY_MSG02,
PAIR_VERIFY_MSG03,
PAIR_VERIFY_MSG04,
PAIR_CONTROL_WRITE,
PAIR_CONTROL_READ,
PAIR_EVENTS_WRITE,
PAIR_EVENTS_READ,
};
struct pair_keys_map {
uint8_t state;
const char *salt;
const char *info;
const char nonce[8];
};
static struct pair_keys_map pair_keys_map[] = {
// Used for /pair-setup
{0x01, NULL, NULL, ""},
{0x02, NULL, NULL, ""},
{0x03, NULL, NULL, ""},
{0x04, NULL, NULL, ""},
{0x05, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg05"},
{0x06, "Pair-Setup-Encrypt-Salt", "Pair-Setup-Encrypt-Info", "PS-Msg06"},
{0, "Pair-Setup-Controller-Sign-Salt", "Pair-Setup-Controller-Sign-Info", ""},
// Used for /pair-verify
{0x01, NULL, NULL, ""},
{0x02, "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", "PV-Msg02"},
{0x03, "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", "PV-Msg03"},
{0x04, NULL, NULL, ""},
// Encryption/decryption of control channel
{0, "Control-Salt", "Control-Write-Encryption-Key", ""},
{0, "Control-Salt", "Control-Read-Encryption-Key", ""},
// Encryption/decryption of event channel
{0, "Events-Salt", "Events-Write-Encryption-Key", ""},
{0, "Events-Salt", "Events-Read-Encryption-Key", ""},
};
enum pair_method {
PairingMethodPairSetup = 0x00,
PairingMethodPairSetupWithAuth = 0x01,
PairingMethodPairVerify = 0x02,
PairingMethodAddPairing = 0x03,
PairingMethodRemovePairing = 0x04,
PairingMethodListPairings = 0x05
};
enum pair_flags {
PairingFlagsTransient = 0x10,
};
struct verifier_setup_context {
struct pair_definition *type;
struct SRPVerifier *verifier;
char pin[4];
char device_id[17]; // Incl. zero term
uint8_t *pkA;
uint64_t pkA_len;
const uint8_t *pkB;
int pkB_len;
const uint8_t *b;
int b_len;
uint8_t *M1;
uint64_t M1_len;
const uint8_t *M2;
int M2_len;
const uint8_t *v;
int v_len;
const uint8_t *salt;
int 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;
};
// static struct verifier_setup_context server_setup_ctx;
ssize_t write_encrypted(file_cipher_context *context, const void *buf, size_t count) {
// need to make this cancellable!
// encrypt the contents of the buffer
ssize_t response;
uint8_t nonce[NONCE_LENGTH] = {0};
// uint8_t tag[AUTHTAG_LENGTH];
uint8_t *plain_block;
uint8_t *cipher_block;
uint16_t block_len;
int nblocks;
int ret;
int i;
if ((count == 0) || (buf == NULL)) {
debug(1, "encrypting a null character sequence");
response = 0;
} else {
// Encryption is done in blocks, where each block consists of a short, the
// encrypted data and an auth tag. The short is the size of the encrypted
// data. The encrypted data in the block cannot exceed ENCRYPTED_LEN_MAX.
nblocks = 1 + ((count - 1) / ENCRYPTED_LEN_MAX); // Ceiling of division
ssize_t ctl = nblocks * (sizeof(block_len) + AUTHTAG_LENGTH) + count;
// size_t hdh = 0;
void *ciphertext = malloc(ctl);
if (ciphertext != NULL) {
context->cipher_context->encryption_counter_prev =
context->cipher_context->encryption_counter;
for (i = 0, plain_block = (uint8_t *)buf, cipher_block = ciphertext; i < nblocks; i++) {
// If it is the last block we will encrypt only the remaining data
if (i == nblocks - 1) {
uint8_t *b = (uint8_t *)buf;
block_len = b + count - plain_block;
} else
block_len = ENCRYPTED_LEN_MAX;
memcpy(nonce + 4, &(context->cipher_context->encryption_counter),
sizeof(context->cipher_context->encryption_counter)); // TODO BE or LE?
// Write the ciphered block
memcpy(cipher_block, &block_len, sizeof(block_len)); // TODO BE or LE?
unsigned long long cipher_length = 0;
ret = crypto_aead_chacha20poly1305_ietf_encrypt(
cipher_block + sizeof(block_len), // ciphertext
&cipher_length,
plain_block, // message
block_len, // message length
(unsigned char *)&block_len, // additional data
sizeof(block_len), // additional data length
NULL,
nonce, // nonce
context->cipher_context->encryption_key // key
);
if (ret < 0) {
debug(1, "Encryption with chacha poly1305 failed");
context->cipher_context->encryption_counter =
context->cipher_context->encryption_counter_prev;
free(ciphertext);
} else {
plain_block += block_len;
cipher_block += block_len + sizeof(block_len) + AUTHTAG_LENGTH;
context->cipher_context->encryption_counter++;
}
}
// maybe this really should be write_sized_block, similar to read_sized_block
ssize_t trywrite = write(context->fd, ciphertext, ctl);
if (trywrite != ctl)
debug(1, "write_encrypted write failure");
free(ciphertext);
response = count; // pretend only the plaintext number of bytes were written
} else {
// can't allocate memory for the cyphertext
errno = ENOMEM;
response = -1;
}
}
return response;
return buf_remove(&ctx->plain_buf, buf, count);
}
ssize_t read_encrypted(file_cipher_context *context, void *buf, size_t count) {
// need to make this cancellable!
// if the plaintext buffer is empty, this will
// read an encrypted block and decipher it to plaintext
// this will then return bytes in the plaintext buffer
// up to the limit of the count
ssize_t response = 0;
if (context->input_plaintext_buffer == NULL) {
response = -1;
uint16_t block_len;
if (read_sized_block(context->fd, &block_len, sizeof(block_len)) == sizeof(block_len)) {
void *cipher_block = malloc(block_len + AUTHTAG_LENGTH);
if (cipher_block != NULL) {
if (read_sized_block(context->fd, cipher_block, (block_len + AUTHTAG_LENGTH)) ==
block_len + AUTHTAG_LENGTH) {
// here we have the encrypted block
uint8_t nonce[NONCE_LENGTH] = {0};
uint8_t tag[AUTHTAG_LENGTH];
context->input_plaintext_buffer = malloc(block_len); // this should be more than enough
if (context->input_plaintext_buffer != NULL) {
memcpy(tag, cipher_block + block_len, sizeof(tag));
memcpy(nonce + 4, &(context->cipher_context->decryption_counter),
sizeof(context->cipher_context->decryption_counter)); // TODO BE or LE?
unsigned long long new_payload_length = 0;
int ret = crypto_aead_chacha20poly1305_ietf_decrypt(
context->input_plaintext_buffer, // m
&new_payload_length, // mlen_p
NULL, // nsec,
cipher_block, // cipher text
block_len + AUTHTAG_LENGTH, // length of the ciphertext
(unsigned char *)&block_len, // authenticated additional data
sizeof(block_len), // authenticated additional data length
nonce, // nonce
context->cipher_context->decryption_key // key
);
free(cipher_block);
cipher_block = NULL;
if (ret == 0) {
response = 0; // decryption was successful
context->cipher_context->decryption_counter++;
context->input_plaintext_buffer_toq = context->input_plaintext_buffer;
context->input_plaintext_buffer_bytes_occupied = block_len;
} else {
// decryption failed
debug(1, "decryption failed.");
free(context->input_plaintext_buffer);
context->input_plaintext_buffer = NULL;
errno = EILSEQ; // illegal byte sequence
}
} else {
// failed to allocate a plaintext buffer
debug(1, "could not allocate a plaintext buffer");
free(cipher_block);
cipher_block = NULL;
errno = ENOMEM;
}
} else {
// failed to read a block properly -- errno will be set
// debug(1, "could not read a block");
free(cipher_block);
cipher_block = NULL;
}
} else {
// failed to allocate a ciphertext buffer
debug(1, "could not allocate a ciphertext buffer");
errno = ENOMEM;
}
} else {
debug(2, "could not read the block_len");
// failed to read the encrypted block size -- errno will be set
}
static ssize_t write_encrypted(rtsp_conn_info *conn, const void *buf, size_t count) {
uint8_t *encrypted;
size_t encrypted_len;
ssize_t ret = pair_encrypt(&encrypted, &encrypted_len, buf, count, conn->ap2_control_pairing.cipher_ctx);
if (ret < 0) {
debug(1, pair_cipher_errmsg(conn->ap2_control_pairing.cipher_ctx));
return -1;
}
if (response == 0) {
// now, transfer bytes to the output buffer up to the limit of the count requested
if (context->input_plaintext_buffer_bytes_occupied) {
size_t bytes_to_transfer = context->input_plaintext_buffer_bytes_occupied;
if (bytes_to_transfer > count)
bytes_to_transfer = count;
if (bytes_to_transfer == 0)
debug(1, "Shome problem -- zero bytes to transfer!");
memcpy(buf, context->input_plaintext_buffer_toq, bytes_to_transfer);
context->input_plaintext_buffer_bytes_occupied =
context->input_plaintext_buffer_bytes_occupied - bytes_to_transfer;
if (context->input_plaintext_buffer_bytes_occupied == 0) {
free(context->input_plaintext_buffer);
context->input_plaintext_buffer = NULL;
} else {
context->input_plaintext_buffer_toq =
context->input_plaintext_buffer_toq + bytes_to_transfer;
}
response = bytes_to_transfer;
size_t remain = encrypted_len;
while (remain > 0) {
ssize_t wrote = write(conn->fd, encrypted + (encrypted_len - remain), remain);
if (wrote <= 0) {
free(encrypted);
return wrote;
}
remain -= wrote;
}
return response;
free(encrypted);
return count;
}
#endif
ssize_t read_from_rtsp_connection(rtsp_conn_info *conn, void *buf, size_t count) {
#ifdef CONFIG_AIRPLAY_2
if ((conn->control_cipher_context.cipher_context != NULL) &&
(conn->control_cipher_context.active != 0)) {
return read_encrypted(&conn->control_cipher_context, buf, count);
if (conn->ap2_control_pairing.cipher_ctx) {
conn->ap2_control_pairing.is_encrypted = 1;
return read_encrypted(conn->fd, &conn->ap2_control_pairing, buf, count);
} else {
return read(conn->fd, buf, count);
}
@@ -1433,9 +1121,8 @@ int msg_write_response(rtsp_conn_info *conn, rtsp_message *resp) {
#ifdef CONFIG_AIRPLAY_2
ssize_t reply;
if ((conn->control_cipher_context.cipher_context != NULL) &&
(conn->control_cipher_context.active != 0)) {
reply = write_encrypted(&conn->control_cipher_context, pkt, p - pkt);
if (conn->ap2_control_pairing.is_encrypted) {
reply = write_encrypted(conn, pkt, p - pkt);
} else {
reply = write(conn->fd, pkt, p - pkt);
}
@@ -1731,183 +1418,44 @@ void handle_get(__attribute((unused)) rtsp_conn_info *conn, __attribute((unused)
#ifdef CONFIG_AIRPLAY_2
void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
int ret;
resp->respcode = 200; // assume everything works out okay
uint8_t *body;
size_t body_len;
struct pair_result *result;
debug(2, "Connection %d: pair-setup Content-Length %d", conn->connection_number,
req->contentlength);
pair_tlv_values_t *values = pair_tlv_new();
pair_tlv_t *state;
pair_tlv_t *method;
pair_tlv_t *flags;
ret = pair_tlv_parse((const unsigned char *)req->content, req->contentlength, values);
if (ret < 0)
debug(1, "Could not parse TLV");
state = pair_tlv_get_value(values, TLVType_State);
if (!state || state->size != 1) {
debug(1, "Missing/unexpected pairing state in TLV.");
}
if (state->value[0] == pair_keys_map[PAIR_SETUP_MSG01].state) {
debug(2, "pair-setup part 1");
method = pair_tlv_get_value(values, TLVType_Method);
if (!method || method->size != 1 || method->value[0] != 0) {
debug(1, "Missing/unexpected pairing method in TLV.");
}
if (method->value[0] != PairingMethodPairSetup)
debug(1, "Unexpected method value %u.", method->value[0]);
flags = pair_tlv_get_value(values, TLVType_Flags);
if (!flags || flags->size != 1) {
debug(1, "Missing/unexpected pairing flags in TLV.");
}
if (flags->value[0] == PairingFlagsTransient) {
debug(2, "Transient pairing selected.");
conn->pairing_mode =
PairingFlagsTransient; // when pairing step 2 is finished, turn on encryption
}
// Note this is modified to return a 16 byte salt
srp_create_salted_verification_key(
SRP_SHA512, SRP_NG_CUSTOM, USERNAME, (const unsigned char *)PASSWORD, strlen(PASSWORD),
&conn->server_setup_ctx->salt, &conn->server_setup_ctx->salt_len,
&conn->server_setup_ctx->v, &conn->server_setup_ctx->v_len, nl_hex, gl_hex);
srp_verifier_get_B(SRP_SHA512, SRP_NG_CUSTOM, conn->server_setup_ctx->v,
conn->server_setup_ctx->v_len, &conn->server_setup_ctx->b,
&conn->server_setup_ctx->b_len, &conn->server_setup_ctx->pkB,
&conn->server_setup_ctx->pkB_len, nl_hex, gl_hex);
pair_tlv_values_t *payload = pair_tlv_new();
pair_tlv_add_value(payload, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG02].state,
sizeof(pair_keys_map[PAIR_SETUP_MSG02].state));
pair_tlv_add_value(payload, TLVType_Salt, conn->server_setup_ctx->salt,
conn->server_setup_ctx->salt_len); // 16
pair_tlv_add_value(payload, TLVType_PublicKey, conn->server_setup_ctx->pkB,
conn->server_setup_ctx->pkB_len); // 384
// turn the tlv to binary form
char *body = malloc(RESPONSE_BUFSIZE);
size_t body_len;
body_len = RESPONSE_BUFSIZE;
ret = pair_tlv_format(payload, (uint8_t *)body, &body_len);
if (ret < 0)
debug(1, "Can't convert response to a binary stream");
pair_tlv_free(payload);
resp->content = body; // these will be freed when the data is sent
resp->contentlength = body_len;
msg_add_header(resp, "Content-Type", "application/octet-stream");
debug_log_rtsp_message(2, "pair-setup part 1 response", resp);
resp->respcode = 200; // it all worked out okay
} else if (state->value[0] == pair_keys_map[PAIR_SETUP_MSG03].state) {
debug(2, "pair setup part 2");
// uint8_t *out;
// size_t out_len;
const uint8_t *key;
int key_len;
pair_tlv_t *pk;
pair_tlv_t *proof;
pair_tlv_values_t *payload;
pk = pair_tlv_get_value(values, TLVType_PublicKey);
proof = pair_tlv_get_value(values, TLVType_Proof);
if ((pk != NULL) && (proof != NULL)) {
conn->server_setup_ctx->pkA_len = pk->size; // 384
conn->server_setup_ctx->pkA = malloc(conn->server_setup_ctx->pkA_len);
memcpy(conn->server_setup_ctx->pkA, pk->value, conn->server_setup_ctx->pkA_len);
conn->server_setup_ctx->M1_len = proof->size; // 64
conn->server_setup_ctx->M1 = malloc(conn->server_setup_ctx->M1_len);
memcpy(conn->server_setup_ctx->M1, proof->value, conn->server_setup_ctx->M1_len);
conn->server_setup_ctx->verifier = srp_verifier_new(
SRP_SHA512, SRP_NG_CUSTOM, USERNAME, conn->server_setup_ctx->salt,
conn->server_setup_ctx->salt_len, conn->server_setup_ctx->v,
conn->server_setup_ctx->v_len, conn->server_setup_ctx->pkA,
conn->server_setup_ctx->pkA_len, conn->server_setup_ctx->b, conn->server_setup_ctx->b_len,
conn->server_setup_ctx->pkB, conn->server_setup_ctx->pkB_len, nl_hex, gl_hex);
if (conn->server_setup_ctx->verifier != NULL) {
if (!conn->server_setup_ctx->verifier) {
die("Error verifier");
}
conn->server_setup_ctx->M2_len = 64; // 512 bit hash
srp_verifier_verify_session(conn->server_setup_ctx->verifier, conn->server_setup_ctx->M1,
&conn->server_setup_ctx->M2);
if (conn->server_setup_ctx->M2 != NULL) {
key = srp_verifier_get_session_key(conn->server_setup_ctx->verifier, &key_len);
conn->control_cipher_context.cipher_context =
pair_cipher_new(PAIR_SERVER_HOMEKIT_TRANSIENT, 2, key, key_len);
conn->control_cipher_context.fd = conn->fd;
conn->control_cipher_context.input_plaintext_buffer = NULL;
conn->control_cipher_context.input_plaintext_buffer_toq = NULL;
conn->control_cipher_context.input_plaintext_buffer_bytes_occupied = 0;
conn->control_cipher_context.active = 0;
payload = pair_tlv_new();
pair_tlv_add_value(payload, TLVType_State, &pair_keys_map[PAIR_SETUP_MSG04].state,
sizeof(pair_keys_map[PAIR_SETUP_MSG04].state));
pair_tlv_add_value(payload, TLVType_Proof, conn->server_setup_ctx->M2,
conn->server_setup_ctx->M2_len); // 384
char *body = malloc(RESPONSE_BUFSIZE);
size_t body_len;
body_len = RESPONSE_BUFSIZE;
ret = pair_tlv_format(payload, (uint8_t *)body, &body_len);
if (ret < 0)
debug(1, "Can't convert response to a binary stream");
pair_tlv_free(payload);
// free the verifier created by srp_verifier_new()
if (conn->server_setup_ctx->verifier != NULL) {
srp_verifier_delete(conn->server_setup_ctx->verifier);
}
// free mallocs made here for pkA and M1
if (conn->server_setup_ctx->pkA != NULL)
free((void *)conn->server_setup_ctx->pkA);
if (conn->server_setup_ctx->M1 != NULL)
free((void *)conn->server_setup_ctx->M1);
// free mallocs made by srp_create_salted_verification_key()
if (conn->server_setup_ctx->salt != NULL)
free((void *)conn->server_setup_ctx->salt);
if (conn->server_setup_ctx->v != NULL)
free((void *)conn->server_setup_ctx->v);
// free mallocs made by srp_verifier_get_B()
if (conn->server_setup_ctx->b != NULL)
free((void *)conn->server_setup_ctx->b);
// if (conn->server_setup_ctx->pkB != NULL)
// free((void *)conn->server_setup_ctx->pkB);
resp->content = body; // these will be freed when the data is sent
resp->contentlength = body_len;
msg_add_header(resp, "Content-Type", "application/octet-stream");
debug_log_rtsp_message(2, "pair-setup part 2 response", resp);
resp->respcode = 200; // it all worked out okay
} else {
debug(1, "Error M2");
resp->respcode = 451; // 451 is "Parameter not understood"
}
} else {
debug(1, "Error verifier");
resp->respcode = 451;
}
} else {
debug(1, "Error pkA ver");
if (!conn->ap2_control_pairing.setup_ctx) {
conn->ap2_control_pairing.setup_ctx = pair_setup_new(PAIR_SERVER_HOMEKIT, NULL, NULL, NULL, config.airplay_device_id);
if (!conn->ap2_control_pairing.setup_ctx) {
debug(1, "Error creating setup context");
resp->respcode = 451;
return;
}
} else {
debug(1, "don't recognise pair setup message");
}
pair_tlv_free(values);
ret = pair_setup(&body, &body_len, conn->ap2_control_pairing.setup_ctx, (const uint8_t *)req->content, req->contentlength);
if (ret < 0) {
debug(1, pair_setup_errmsg(conn->ap2_control_pairing.setup_ctx));
resp->respcode = 451;
return;
}
ret = pair_setup_result(NULL, &result, conn->ap2_control_pairing.setup_ctx);
if (ret == 0 && result->shared_secret_len > 0) {
// Transient pairing completed (pair-setup step 2), prepare encryption, but
// don't activate yet, the response to this request is still plaintext
conn->ap2_control_pairing.cipher_ctx = pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len);
if (!conn->ap2_control_pairing.cipher_ctx) {
debug(1, "Error setting up rtsp control channel ciphering\n");
resp->respcode = 451;
return;
}
}
resp->content = (char *)body; // these will be freed when the data is sent
resp->contentlength = body_len;
msg_add_header(resp, "Content-Type", "application/octet-stream");
debug_log_rtsp_message(2, "pair-setup response", resp);
}
void handle_fp_setup(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message *req,
@@ -4119,10 +3667,11 @@ void rtsp_conversation_thread_cleanup_function(void *arg) {
conn->client_setup_plist = NULL;
}
// free the cipher context, if allocated
if (conn->control_cipher_context.cipher_context)
free(conn->control_cipher_context.cipher_context);
buf_drain(&conn->ap2_control_pairing.plain_buf, -1);
buf_drain(&conn->ap2_control_pairing.encrypted_buf, -1);
pair_setup_free(conn->ap2_control_pairing.setup_ctx);
pair_verify_free(conn->ap2_control_pairing.verify_ctx);
pair_cipher_free(conn->ap2_control_pairing.cipher_ctx);
#endif
rtp_terminate(conn);
@@ -4163,11 +3712,6 @@ void rtsp_conversation_thread_cleanup_function(void *arg) {
debug(3, "Connection %d: Checking play lock.", conn->connection_number);
release_play_lock(conn);
#ifdef CONFIG_AIRPLAY_2
if (conn->server_setup_ctx)
free(conn->server_setup_ctx);
#endif
debug(1, "Connection %d: terminated.", conn->connection_number);
conn->running = 0;
pthread_setcancelstate(oldState, NULL);
@@ -4213,20 +3757,10 @@ static void *rtsp_conversation_thread_func(void *pconn) {
#ifdef CONFIG_AIRPLAY_2
conn->ap2_audio_buffer_size = 1024 * 1024 * 8;
conn->server_setup_ctx =
(struct verifier_setup_context *)malloc(sizeof(struct verifier_setup_context));
if (conn->server_setup_ctx != NULL)
memset(conn->server_setup_ctx, 0, sizeof(struct verifier_setup_context));
#endif
while (conn->stop == 0) {
int debug_level = 3; // for printing the request and response
#ifdef CONFIG_AIRPLAY_2
// transactions should be begin to be encrypted after a read/response cycle has
// been completed
conn->control_cipher_context.active = 1;
#endif
reply = rtsp_read_request(conn, &req);
if (reply == rtsp_read_request_response_ok) {
pthread_cleanup_push(msg_cleanup_function, (void *)&req);