sout: add airplay support

parent 5b6c8b9d
......@@ -4072,6 +4072,11 @@ AS_IF([test "${enable_gnutls}" != "no"], [
])
dnl
dnl Airplay plugin
dnl
PKG_ENABLE_MODULES_VLC([AIRPLAY], [stream_out_airplay], [libplist libplist >= 2.0.0], [Airplay support], [auto], [${GCRYPT_CFLAGS} ${SOCKET_LIBS}], [${LIBPLIST_LIBS} ${GCRYPT_LIBS}])
dnl
dnl Taglib plugin
dnl
......
......@@ -367,6 +367,7 @@ $Id$
* stats: Stats encoder function
* stereo_widen: Enhances stereo effect
* stl: EBU STL decoder
* stream_out_airplay: AirPlay streaming output module
* stream_out_autodel: monitor mux inputs and automatically add/delete streams
* stream_out_bridge: "exchange" streams between sout instances. To be used with VLM
* stream_out_chromaprint: Audio fingerprinter
......
......@@ -73,6 +73,14 @@ libstream_out_chromaprint_plugin_la_LIBADD = $(CHROMAPRINT_LIBS)
EXTRA_LTLIBRARIES += libstream_out_chromaprint_plugin.la
sout_LTLIBRARIES += $(LTLIBstream_out_chromaprint)
# AirPlay plugin
if HAVE_GCRYPT
libstream_out_airplay_plugin_la_SOURCES = stream_out/airplay/airplay.c stream_out/airplay/csrp/srp.c stream_out/airplay/csrp/srp.h
libstream_out_airplay_plugin_la_CFLAGS = $(GCRYPT_CFLAGS) $(LIBPLIST_CFLAGS) $(AM_CFLAGS) $(CFLAGS_stream_out_airplay)
libstream_out_airplay_plugin_la_LIBADD = $(GCRYPT_LIBS) -lplist $(LIBS_stream_out_airplay)
sout_LTLIBRARIES += libstream_out_airplay_plugin.la
endif
# Chromecast plugin
SUFFIXES += .proto .pb.cc
......
This diff is collapsed.
/*
* A Secure Remote Password 6a implementation (RFC 5054)
* Also includes Apple-Flavoured SRP which changes a
* few of the functions to be compatible with Apple TVs.
*
* Adapted from CSRP by Tom Cocagne by Alexander Lyon.
* https://github.com/cocagne/csrp
*
* The MIT License (MIT)
*
* Copyright (c) 2013 Tom Cocagne, Alexander Lyon
*
* 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.
*/
/*
* Below are the symbols used in the code:
*
* Symbol | Description
* --------- | -----------
* H(x) | One-way hash function (SHA1)
* PAD(x, y) | Left pad x to nearest byte, length y
* ^ | Exponentiation
* ~ | Concatenation
*
* Symbol | Source | Calculation | Description
* -- | ------------------ | ------------------------ | --------------------------------
* N | Predetermined | | A large safe prime (N = 2q+1, where q is prime)
* g | Predetermined | | A generator
* a | Generated by Client | | Client ephemeral secret key
* b | Generated by Server | | Server ephemeral secret key
* I | Supplied by User | | Username or Identity
* P | Supplied by User | | Cleartext Password
* s | Generated by Server | | Salt
* k | Calculated | `H(PAD(N, N) ~ PAD(g, N))` | SRP-6 multiplier
* x | Calculated | `H(s ~ H( I ~ ':' ~ P ))` | Used to calculate the verifier and shared session key
* v | Calculated | `g^x % N` | Server password verifier
* B | Calculated by Server | `(k * v + g^b) % N` | Server ephemeral public key
* A | Calculated by Client | `g^a % N` | Client ephemeral public key
* u | Calculated | `H(A ~ B)` | Random scrambling parameter
* S<sub>Client</sub> | Calculated | `(B - k * g^x) ^ (a + u * x) % N` |
* S<sub>Server</sub> | Calculated | `(A * v^u) ^ b % N` |
* K | Calculated | `H(H(S) ~ 00000000) ~ H(H(S) ~ 00000001)` | Shared Key
* M1 | Calculated | `H(H(N) xor H(g) ~ H(I) ~ s ~ A ~ B ~ K)` | Client key verifier
* M2 | Calculated | `H(A ~ M1 ~ K)` | Server key verifier
*/
#include <assert.h>
#include <gcrypt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "srp.h"
typedef struct
{
gcry_mpi_t N;
gcry_mpi_t g;
} Ng_pair_t;
typedef struct
{
const char *psz_N;
const char *psz_g;
} Ng_hex_pair_t;
/* All constants here were pulled from Appendix A of RFC 5054 */
static Ng_hex_pair_t Ng_hex_pairs[] = {
{
/* 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"
},
{
/* null sentinel */
0,
0
}
};
static Ng_pair_t *new_ng( srp_Ng_type pair_type, const char *psz_N_hex, const char *psz_g_hex )
{
Ng_pair_t *ng = malloc( sizeof( *ng ) );
const char *p_source = psz_N_hex == NULL ? Ng_hex_pairs[pair_type].psz_N : psz_N_hex;
const char *g_source = psz_g_hex == NULL ? Ng_hex_pairs[pair_type].psz_g : psz_g_hex;
gcry_error_t error = 0;
error |= gcry_mpi_scan( &ng->N, GCRYMPI_FMT_HEX, p_source, 0, NULL );
error |= gcry_mpi_scan( &ng->g, GCRYMPI_FMT_HEX, g_source, 0, NULL );
return error == 0 ? ng : NULL;
}
static void delete_ng( Ng_pair_t **pp_pair )
{
if ( *pp_pair != NULL )
{
gcry_mpi_release( ( *pp_pair )->N );
gcry_mpi_release( ( *pp_pair )->g );
free( *pp_pair );
}
*pp_pair = NULL;
}
struct srp_verifier
{
srp_hash_algorithm hash_alg;
Ng_pair_t *p_Ng_pair;
bool b_authenticated;
char *psz_username;
uint8_t *p_bytes_B;
uint8_t *p_bytes_M1;
uint8_t *p_bytes_M2;
uint8_t *p_bytes_K;
};
struct srp_user
{
srp_hash_algorithm hash_alg;
Ng_pair_t *p_ng;
gcry_mpi_t a;
gcry_mpi_t A;
gcry_mpi_t S;
const uint8_t *p_bytes_A;
bool b_authenticated;
char *psz_username;
char *psz_password;
size_t i_password_len;
size_t i_username_len;
uint8_t *p_bytes_M1; // client verifier
uint8_t *p_bytes_M2; // server verifier
uint8_t *p_bytes_K; // shared key
size_t i_length_K;
void (*fp_calculate_K)( srp_hash_algorithm alg, gcry_mpi_t S, uint8_t **pp_bytes_K, size_t *p_i_length_K );
void (*fp_calculate_M1)( struct srp_user *p_user, gcry_mpi_t s, gcry_mpi_t B, uint8_t **pp_bytes_M1,
size_t *pi_length_M1 );
};
static int srp_hash_to_gcrypt( srp_hash_algorithm alg )
{
switch ( alg )
{
case SRP_SHA1 :
return GCRY_MD_SHA1;
case SRP_SHA224:
return GCRY_MD_SHA224;
case SRP_SHA256:
return GCRY_MD_SHA256;
case SRP_SHA384:
return GCRY_MD_SHA384;
case SRP_SHA512:
return GCRY_MD_SHA512;
default:
return -1;
};
}
static size_t mpi_nbytes( gcry_mpi_t mpi )
{
return ( gcry_mpi_get_nbits( mpi ) + 7 ) / 8;
}
// H(p_bytes_in)
static void hash_bytes( srp_hash_algorithm alg, const uint8_t *p_bytes_in, size_t i_length, uint8_t **pp_bytes_out )
{
size_t i_digest_size = srp_hash_length( alg );
*pp_bytes_out = calloc( i_digest_size, sizeof( **pp_bytes_out ) );
gcry_md_hash_buffer( srp_hash_to_gcrypt( alg ), *pp_bytes_out, p_bytes_in, i_length );
}
// H(mpi_to_bin(n))
static void hash_mpi( srp_hash_algorithm alg, gcry_mpi_t n, uint8_t **pp_bytes_out, size_t *pi_length_out )
{
size_t i_length = mpi_nbytes( n );
if ( pi_length_out != NULL ) *pi_length_out = i_length;
uint8_t *p_bytes = calloc( i_length, sizeof( *p_bytes ) );
gcry_mpi_print( GCRYMPI_FMT_USG, p_bytes, i_length, NULL, n );
hash_bytes( alg, p_bytes, i_length, pp_bytes_out );
free( p_bytes );
}
// H(mpi_to_bin(n1) | mpi_to_bin(n2))
static gcry_mpi_t *hash_mpi_concat_mpi( srp_hash_algorithm alg, gcry_mpi_t n1, gcry_mpi_t n2 )
{
uint8_t *p_hashed_bytes = NULL;
size_t i_len_n1 = mpi_nbytes( n1 );
size_t i_len_n2 = mpi_nbytes( n2 );
uint8_t *n_n1_bytes = calloc( i_len_n1, sizeof( *n_n1_bytes ) );
uint8_t *n_n2_bytes = calloc( i_len_n2, sizeof( *n_n2_bytes ) );
gcry_mpi_print( GCRYMPI_FMT_USG, n_n1_bytes, i_len_n1, NULL, n1 );
gcry_mpi_print( GCRYMPI_FMT_USG, n_n2_bytes, i_len_n2, NULL, n2 );
gcry_md_hd_t digest_handle;
gcry_md_open( &digest_handle, srp_hash_to_gcrypt( alg ), 0 );
gcry_md_write( digest_handle, n_n1_bytes, i_len_n1 );
gcry_md_write( digest_handle, n_n2_bytes, i_len_n2 );
p_hashed_bytes = gcry_md_read( digest_handle, srp_hash_to_gcrypt( alg ) );
gcry_mpi_t *ret = malloc( sizeof( *ret ) );
gcry_mpi_scan( ret, GCRYMPI_FMT_USG, p_hashed_bytes, srp_hash_length( alg ), NULL );
gcry_md_close( digest_handle );
free( n_n1_bytes );
free( n_n2_bytes );
return ret;
}
// H(PAD(n1) | PAD(n2))
static gcry_mpi_t *hash_mpi_concat_mpi_padded( srp_hash_algorithm alg, size_t i_pad_length,
gcry_mpi_t n1, gcry_mpi_t n2 )
{
uint8_t *p_hashed_bytes = NULL;
size_t i_len_n1 = mpi_nbytes( n1 );
size_t i_len_n2 = mpi_nbytes( n2 );
assert( i_len_n1 <= i_pad_length );
assert( i_len_n2 <= i_pad_length );
uint8_t *n_n1_bytes = calloc( i_pad_length, sizeof( *n_n1_bytes ) );
uint8_t *n_n2_bytes = calloc( i_pad_length, sizeof( *n_n2_bytes ) );
gcry_mpi_print( GCRYMPI_FMT_USG, n_n1_bytes + i_pad_length - i_len_n1, i_len_n1, NULL, n1 );
gcry_mpi_print( GCRYMPI_FMT_USG, n_n2_bytes + i_pad_length - i_len_n2, i_len_n2, NULL, n2 );
gcry_md_hd_t digest_handle;
gcry_md_open( &digest_handle, srp_hash_to_gcrypt( alg ), 0 );
gcry_md_write( digest_handle, n_n1_bytes, i_pad_length );
gcry_md_write( digest_handle, n_n2_bytes, i_pad_length );
p_hashed_bytes = gcry_md_read( digest_handle, srp_hash_to_gcrypt( alg ) );
gcry_mpi_t *ret = malloc( sizeof( *ret ) );
gcry_mpi_scan( ret, GCRYMPI_FMT_USG, p_hashed_bytes, srp_hash_length( alg ), NULL );
gcry_md_close( digest_handle );
free( n_n1_bytes );
free( n_n2_bytes );
return ret;
}
// H(n | bytes)
static gcry_mpi_t *hash_mpi_concat_bytes( srp_hash_algorithm alg, gcry_mpi_t n, const uint8_t *p_bytes,
size_t i_len_bytes )
{
uint8_t *p_hashed_bytes = NULL;
size_t i_len_n = mpi_nbytes( n );
uint8_t *p_n_bytes = calloc( i_len_n, sizeof( *p_n_bytes ) );
gcry_mpi_print( GCRYMPI_FMT_USG, p_n_bytes, i_len_n, NULL, n );
gcry_md_hd_t digest_handle;
gcry_md_open( &digest_handle, srp_hash_to_gcrypt( alg ), 0 );
gcry_md_write( digest_handle, p_n_bytes, i_len_n );
gcry_md_write( digest_handle, p_bytes, i_len_bytes );
p_hashed_bytes = gcry_md_read( digest_handle, srp_hash_to_gcrypt( alg ) );
gcry_mpi_t *ret = malloc( sizeof( *ret ) );
gcry_mpi_scan( ret, GCRYMPI_FMT_USG, p_hashed_bytes, srp_hash_length( alg ), NULL );
gcry_md_close( digest_handle );
free( p_n_bytes );
return ret;
}
// H(s | H(I | ":" | P))
static gcry_mpi_t *calculate_x( srp_hash_algorithm alg, gcry_mpi_t salt,
const char *psz_username, size_t i_username_len,
const char *psz_password, size_t i_password_len )
{
gcry_md_hd_t digest_handle;
gcry_md_open( &digest_handle, srp_hash_to_gcrypt( alg ), 0 );
gcry_md_write( digest_handle, psz_username, i_username_len );
gcry_md_write( digest_handle, ":", 1 );
gcry_md_write( digest_handle, psz_password, i_password_len );
gcry_mpi_t *ret = hash_mpi_concat_bytes(
alg, salt,
gcry_md_read( digest_handle, srp_hash_to_gcrypt( alg ) ),
gcry_md_get_algo_dlen( gcry_md_get_algo( digest_handle ) )
);
gcry_md_close( digest_handle );
return ret;
}
// updates the digest handle with the mpi in p_n
static void md_write_mpi( gcry_md_hd_t p_digest_handle, gcry_mpi_t n )
{
size_t len = mpi_nbytes( n );
uint8_t *p_bytes = calloc( len, sizeof( *p_bytes ) );
gcry_mpi_print( GCRYMPI_FMT_USG, p_bytes, len, NULL, n );
gcry_md_write( p_digest_handle, p_bytes, len );
free( p_bytes );
}
static void srp_calculate_K( srp_hash_algorithm alg, gcry_mpi_t S, uint8_t **pp_bytes_K, size_t *pi_length_K )
{
return hash_mpi( alg, S, pp_bytes_K, pi_length_K );
}
// H(S ~ 00 00 00 00) ~ H(S ~ 00 00 00 01)
static void apple_calculate_K( srp_hash_algorithm alg, gcry_mpi_t S, uint8_t **pp_bytes_K, size_t *pi_length_K )
{
uint8_t first_bytes[4] = {0, 0, 0, 0};
uint8_t second_bytes[4] = {0, 0, 0, 1};
size_t i_hash_length = srp_hash_length( alg );
*pp_bytes_K = malloc( i_hash_length * 2 * sizeof( **pp_bytes_K ) );
if ( pi_length_K != NULL ) *pi_length_K = i_hash_length * 2;
gcry_mpi_t *p_K1 = hash_mpi_concat_bytes( alg, S, first_bytes, 4 );
gcry_mpi_t *p_K2 = hash_mpi_concat_bytes( alg, S, second_bytes, 4 );
uint8_t p_K1_bytes[i_hash_length];
uint8_t p_K2_bytes[i_hash_length];
gcry_mpi_print( GCRYMPI_FMT_USG, p_K1_bytes, i_hash_length, NULL, *p_K1 );
gcry_mpi_print( GCRYMPI_FMT_USG, p_K2_bytes, i_hash_length, NULL, *p_K2 );
memcpy( *pp_bytes_K, p_K1_bytes, i_hash_length );
memcpy( *pp_bytes_K + i_hash_length, p_K2_bytes, i_hash_length );
gcry_mpi_release( *p_K1 );
gcry_mpi_release( *p_K2 );
}
// S = ((B - k * (g^x % N)) ^ (a + u * x)) % N
static void calculate_S( const struct srp_user *p_user, const gcry_mpi_t *B, const gcry_mpi_t *u, const gcry_mpi_t *x,
const gcry_mpi_t *k, gcry_mpi_t *S )
{
gcry_mpi_t tmp1 = gcry_mpi_new( 128 );
gcry_mpi_t tmp2 = gcry_mpi_new( 128 );
gcry_mpi_t tmp3 = gcry_mpi_new( 128 );
gcry_mpi_mul( tmp1, *u, *x ); // u * x
gcry_mpi_add( tmp2, p_user->a, tmp1 ); // a + u * x
gcry_mpi_powm( tmp1, p_user->p_ng->g, *x, p_user->p_ng->N ); // g^x % N
gcry_mpi_mul( tmp3, *k, tmp1 ); // k * (g^x % N)
gcry_mpi_sub( tmp1, *B, tmp3 ); // B - k * (g^x % N)
gcry_mpi_powm( *S, tmp1, tmp2, p_user->p_ng->N ); // ((B - k * (g^x % N)) ^ (a + u * x)) % N
gcry_mpi_release( tmp1 );
gcry_mpi_release( tmp2 );
gcry_mpi_release( tmp3 );
}
// p_bytes_M1 = H(A | B | S)
static void
srp_calculate_M1( struct srp_user *p_user, gcry_mpi_t s, gcry_mpi_t B, uint8_t **pp_bytes_M1, size_t *pi_length_M1 )
{
(void)( s );
gcry_md_hd_t digest_handle;
int gcrypt_alg = srp_hash_to_gcrypt( p_user->hash_alg );
gcry_md_open( &digest_handle, gcrypt_alg, 0 );
md_write_mpi( digest_handle, p_user->A );
md_write_mpi( digest_handle, B );
md_write_mpi( digest_handle, p_user->S );
const uint8_t *p_hashed = gcry_md_read( digest_handle, gcrypt_alg );
if ( pi_length_M1 != NULL )
{
*pi_length_M1 = srp_hash_length( p_user->hash_alg );
}
*pp_bytes_M1 = malloc( *pi_length_M1 * sizeof( **pp_bytes_M1 ) );
memcpy( *pp_bytes_M1, p_hashed, *pi_length_M1 );
gcry_md_close( digest_handle );
}
// p_bytes_M1 = H(H(N) xor H(g) ~ H(I) ~ s ~ A ~ B ~ K)
static void
apple_calculate_M1( struct srp_user *p_user, gcry_mpi_t s, gcry_mpi_t B, uint8_t **pp_bytes_M1, size_t *pi_length_M1 )
{
size_t i_digest_length = srp_hash_length( p_user->hash_alg );
gcry_md_hd_t digest_handle;
uint8_t *H_N = NULL;
uint8_t *H_g = NULL;
uint8_t *H_I = NULL;
uint8_t *HN_xor_Hg = calloc( i_digest_length, sizeof( *HN_xor_Hg ) );
hash_mpi( p_user->hash_alg, p_user->p_ng->N, &H_N, NULL );
hash_mpi( p_user->hash_alg, p_user->p_ng->g, &H_g, NULL );
hash_bytes( p_user->hash_alg, (const uint8_t *)p_user->psz_username, strlen( p_user->psz_username ), &H_I );
for ( size_t i = 0; i < i_digest_length; i++ )
HN_xor_Hg[i] = H_N[i] ^ H_g[i];
int gcrypt_alg = srp_hash_to_gcrypt( p_user->hash_alg );
uint8_t *p_bytes_K;
srp_user_get_hashed_session_key( p_user, &p_bytes_K, NULL );
gcry_md_open( &digest_handle, gcrypt_alg, 0 );
gcry_md_write( digest_handle, HN_xor_Hg, i_digest_length );
gcry_md_write( digest_handle, H_I, i_digest_length );
md_write_mpi( digest_handle, s );
md_write_mpi( digest_handle, p_user->A );
md_write_mpi( digest_handle, B );
gcry_md_write( digest_handle, p_bytes_K, i_digest_length * 2 );
const uint8_t *p_hashed = gcry_md_read( digest_handle, gcrypt_alg );
if ( pi_length_M1 != NULL )
{
*pi_length_M1 = i_digest_length;
}
*pp_bytes_M1 = malloc( i_digest_length * sizeof( **pp_bytes_M1 ) );
memcpy( *pp_bytes_M1, p_hashed, i_digest_length );
gcry_md_close( digest_handle );
free( p_bytes_K );
}
// p_bytes_M2 = H(A ~ p_bytes_M1 ~ K)
static void
calculate_M2( srp_hash_algorithm alg, gcry_mpi_t A, const uint8_t *p_bytes_M1, const uint8_t *p_bytes_K,
uint8_t **pp_bytes_M2, size_t *pi_length_M2 )
{
gcry_md_hd_t digest_handle;
size_t i_digest_length = srp_hash_length( alg );
int gcrypt_alg = srp_hash_to_gcrypt( alg );
gcry_md_open( &digest_handle, gcrypt_alg, 0 );
md_write_mpi( digest_handle, A );
gcry_md_write( digest_handle, p_bytes_M1, i_digest_length );
gcry_md_write( digest_handle, p_bytes_K, i_digest_length * 2 );
const uint8_t *p_hashed = gcry_md_read( digest_handle, gcrypt_alg );
if ( pi_length_M2 != NULL )
{
*pi_length_M2 = i_digest_length;
}
*pp_bytes_M2 = malloc( i_digest_length * sizeof( **pp_bytes_M2 ) );
memcpy( *pp_bytes_M2, p_hashed, i_digest_length );
gcry_md_close( digest_handle );
}
/***********************************************************************************************************
*
* Exported Functions
*
***********************************************************************************************************/
size_t srp_hash_length( srp_hash_algorithm alg )
{
return gcry_md_get_algo_dlen( srp_hash_to_gcrypt( alg ) );
}
// Verifier code will be used for airplay receiving.
// It currently is not being used and may be wrong.
//
//void srp_create_salted_verification_key( srp_hash_algorithm alg, srp_Ng_type Ng_type,
// const char *psz_username, size_t i_length_username,
// const char *psz_password, size_t i_length_password,
// uint8_t **pp_bytes_s, size_t *pi_length_s,
// uint8_t **pp_bytes_v, size_t *pi_length_v,
// const char *psz_N_hex, const char *psz_g_hex )
//{
// gcry_mpi_t s = gcry_mpi_new( 256 );
// gcry_mpi_t v = gcry_mpi_new( 256 );
// gcry_mpi_t *x = NULL;
// Ng_pair_t *ng = new_ng( Ng_type, psz_N_hex, psz_g_hex );
//
// if ( !s || !v || !ng )
// goto cleanup_and_exit;
//
// gcry_mpi_randomize( s, 32, GCRY_STRONG_RANDOM );
//
// x = calculate_x( alg, s, psz_username, i_length_username, psz_password, i_length_password );
//
// gcry_mpi_powm( v, ng->g, *x, ng->N );
//
// *pi_length_s = mpi_nbytes( s );
// *pi_length_v = mpi_nbytes( v );
// *pp_bytes_s = malloc( *pi_length_s * sizeof( **pp_bytes_s ) );
// *pp_bytes_v = malloc( *pi_length_v * sizeof( **pp_bytes_v ) );
//
// gcry_mpi_print( GCRYMPI_FMT_USG, *pp_bytes_s, *pi_length_s, NULL, s );
// gcry_mpi_print( GCRYMPI_FMT_USG, *pp_bytes_v, *pi_length_v, NULL, v );
//
// cleanup_and_exit:
// delete_ng( &ng );
// gcry_mpi_release( s );
// gcry_mpi_release( v );
// gcry_mpi_release( *x );
//}
//
//
///* Out: p_bytes_B, len_B.
// *