]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Bring in files and settings from sps2-ex1
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Tue, 27 Apr 2021 19:07:50 +0000 (20:07 +0100)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Tue, 27 Apr 2021 19:07:50 +0000 (20:07 +0100)
.gitignore
.gitmodules
configure.ac
csrp/LICENSE [new file with mode: 0644]
csrp/README.md [new file with mode: 0644]
csrp/srp.c [new file with mode: 0644]
csrp/srp.h [new file with mode: 0644]
plists/get_info_response.xml [new file with mode: 0644]
ptp-utilities.c [new file with mode: 0644]
ptp-utilities.h [new file with mode: 0644]

index 9d13084c7d5b9c834a2d374cf5f89942ea0ae982..5187a2b8de633c6bef86e8e9c0fcb57b819e59f8 100644 (file)
@@ -5,7 +5,6 @@
 /shairport-sync-mpris-test-client
 
 *.o
-*.log
 /*~
 *.xml~
 /config.mk
@@ -33,6 +32,7 @@ shairport-sync.core
 /org.gnome.ShairportSync.service
 /dbus-interface.*
 /mpris-interface.*
+/plist_xml_strings.*
 /lib_*.*
 
 # Some eclipse project files
index 9d79c516a25baa11f1b5e355172c82f62043f1f5..a2828361cd6ee5d35ea3f16ab561f0c2526f382d 100644 (file)
@@ -1,6 +1,7 @@
 [submodule "tinyhttp"]
        path = tinyhttp
        url = https://github.com/mendsley/tinyhttp.git
+       ignore = dirty
 [submodule "pair_ap"]
        path = pair_ap
        url = https://github.com/ejurgensen/pair_ap
index 79a08b4bbd3a9f0d3a8bf627ff3ed9778ce542a5..36d8bec3d25f79d38fff592714fd56a1971e9b87 100644 (file)
@@ -378,7 +378,7 @@ if test "x$with_airplay_2" = "xyes" ; then
   AC_MSG_RESULT(>>Include libraries required for  AirPlay 2)
   AC_CHECK_LIB([plist], [plist_from_memory], , AC_MSG_ERROR(AirPlay 2 support requires libplist -- libplist-dev suggested!))
   AC_CHECK_LIB([sodium], [sodium_init], ,[AC_MSG_ERROR([AirPlay 2 support requires libsodium -- libsodium-dev suggested])])
-  AC_CHECK_LIB([avutil],[av_malloc], [], [AirPlay([Airplay 2 support requires libavutil -- libavutil-dev suggested])])
+  AC_CHECK_LIB([avutil],[av_malloc], [], [AC_MSG_ERROR([Airplay 2 support requires libavutil -- libavutil-dev suggested])])
   AC_CHECK_LIB([avcodec],[avcodec_find_encoder], [], [AC_MSG_ERROR([AirPlay 2 support requires libavcodec -- libavcodec-dev suggested])])
   AC_CHECK_LIB([avformat],[avformat_new_stream], [], [AC_MSG_ERROR([AirPlay 2 support requires libavformat -- libavformat-dev suggested])])
   AC_CHECK_LIB([swresample],[swr_convert], [], [AC_MSG_ERROR([AirPlay 2 support requires libswresample -- libswresample-dev suggested])])
diff --git a/csrp/LICENSE b/csrp/LICENSE
new file mode 100644 (file)
index 0000000..030645d
--- /dev/null
@@ -0,0 +1,21 @@
+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.
diff --git a/csrp/README.md b/csrp/README.md
new file mode 100644 (file)
index 0000000..4e655bc
--- /dev/null
@@ -0,0 +1,155 @@
+csrp
+====
+Tom Cocagne &lt;tom.cocagne@gmail.com&gt;
+
+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;
+}
+```
diff --git a/csrp/srp.c b/csrp/srp.c
new file mode 100644 (file)
index 0000000..1bfd908
--- /dev/null
@@ -0,0 +1,955 @@
+/*
+ * 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;
+}
diff --git a/csrp/srp.h b/csrp/srp.h
new file mode 100644 (file)
index 0000000..4288569
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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 */
diff --git a/plists/get_info_response.xml b/plists/get_info_response.xml
new file mode 100644 (file)
index 0000000..7a3b7a0
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Comment -->
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>audioLatencies</key>
+       <array>
+               <dict>
+                       <key>inputLatencyMicros</key>
+                       <integer>0</integer>
+                       <key>outputLatencyMicros</key>
+                       <integer>400000</integer>
+                       <key>type</key>
+                       <integer>100</integer>
+               </dict>
+               <dict>
+                       <key>audioType</key>
+                       <string>default</string>
+                       <key>inputLatencyMicros</key>
+                       <integer>0</integer>
+                       <key>outputLatencyMicros</key>
+                       <integer>400000</integer>
+                       <key>type</key>
+                       <integer>100</integer>
+               </dict>
+               <dict>
+                       <key>audioType</key>
+                       <string>media</string>
+                       <key>inputLatencyMicros</key>
+                       <integer>0</integer>
+                       <key>outputLatencyMicros</key>
+                       <integer>400000</integer>
+                       <key>type</key>
+                       <integer>100</integer>
+               </dict>
+               <dict>
+                       <key>audioType</key>
+                       <string>media</string>
+                       <key>inputLatencyMicros</key>
+                       <integer>0</integer>
+                       <key>outputLatencyMicros</key>
+                       <integer>400000</integer>
+                       <key>type</key>
+                       <integer>102</integer>
+               </dict>
+       </array>
+       <key>keepAliveLowPower</key>
+       <true/>
+       <key>keepAliveSendStatsAsBody</key>
+       <true/>
+       <key>manufacturer</key>
+       <string>Shairport Sync</string>
+       <key>nameIsFactoryDefault</key>
+       <false/>
+       <key>protocolVersion</key>
+       <string>1.1</string>
+       <key>sdk</key>
+       <string>AirPlay;2.0.2</string>
+       <key>sourceVersion</key>
+       <string>366.0</string>
+       <key>statusFlags</key>
+       <integer>4</integer>
+       <key>txtAirPlay</key>
+       <!-- key values for deviceID, features, pi, name and model are added programatically -->
+</dict>
+</plist>
diff --git a/ptp-utilities.c b/ptp-utilities.c
new file mode 100644 (file)
index 0000000..53dbd4d
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * This file is part of Shairport Sync.
+ * Copyright (c) Mike Brady 2020 -- 2021
+ * All rights reserved.
+ *
+ * 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#define __STDC_FORMAT_MACROS
+#include "common.h"
+#include <inttypes.h>
+#include <unistd.h>
+
+#include "../nqptp/nqptp-shm-structures.h"
+#include "ptp-utilities.h"
+
+int shm_fd;
+void *mapped_addr = NULL;
+
+int failure_message_sent = 0;
+
+static pthread_mutex_t ptp_access_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// returns a copy of the shared memory data from the nqptp
+// shared memory interface, so long as it's open.
+int get_nqptp_data(struct shm_structure *nqptp_data) {
+  int response = -1; // presume the worst. Fix it on success
+  // the first part of the shared memory is a mutex lock, so use it to get
+  // exclusive access while copying
+
+  if ((mapped_addr != MAP_FAILED) && (mapped_addr != NULL)) {
+    pthread_cleanup_debug_mutex_lock((pthread_mutex_t *)mapped_addr, 100000, 1);
+    memcpy(nqptp_data, (char *)mapped_addr, sizeof(struct shm_structure));
+    pthread_cleanup_pop(1); // release the mutex
+    response = 0;
+  }
+  return response;
+}
+
+int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset) {
+  int response = -1;
+  pthread_cleanup_debug_mutex_lock(&ptp_access_mutex, 10000, 1);
+  // 0 -> valid and working; -1 -> can't connect to nqptp
+  if (actual_clock_id != NULL)
+    *actual_clock_id = 0;
+  if (raw_offset != NULL)
+    *raw_offset = 0;
+
+  if (ptp_shm_interface_open() == 0) {
+    struct shm_structure nqptp_data;
+    if (get_nqptp_data(&nqptp_data) == 0) {
+      if (nqptp_data.version == NQPTP_SHM_STRUCTURES_VERSION) {
+      if (actual_clock_id != NULL)
+        *actual_clock_id = nqptp_data.master_clock_id;
+      if (raw_offset != NULL)
+        *raw_offset = nqptp_data.local_to_master_time_offset;
+      response = 0;
+      } else {
+        if (failure_message_sent == 0) {
+          warn("This version of Shairport Sync requires an NQPTP with a Shared Memory Interface Version %u, but the installed version is %u. Please install the correct version of NQPTP.", NQPTP_SHM_STRUCTURES_VERSION, nqptp_data.version);
+          failure_message_sent = 1;
+        }
+      }
+    }
+    if (response != -1)
+      response = ptp_shm_interface_close();
+  } else {
+    if (failure_message_sent == 0) {
+      warn("Can't open the interface to nqptp. Is the service running?");
+      failure_message_sent = 1;
+    }
+  }
+  pthread_cleanup_pop(1); // release the mutex
+  if (response == 0)
+    failure_message_sent = 0;
+  return response;
+}
+
+int ptp_shm_interface_open() {
+  mapped_addr = NULL;
+  int shared_memory_file_descriptor = shm_open("/nqptp", O_RDWR, 0);
+  int response = -1;
+  if (shared_memory_file_descriptor >= 0) {
+    mapped_addr =
+        // needs to be PROT_READ | PROT_WRITE to allow the mapped memory to be writable for the
+        // mutex to lock and unlock
+        mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE, MAP_SHARED,
+             shared_memory_file_descriptor, 0);
+    if (mapped_addr == MAP_FAILED) {
+      if (failure_message_sent == 0) {
+        debug(1, "unable to open the shared memory interface with nqptp. Is the service running?");
+        failure_message_sent = 1;
+      }
+    }
+    if (close(shared_memory_file_descriptor) == -1) {
+      if (failure_message_sent == 0) {
+        debug(1, "error closing \"/nqptp\" after mapping it.");
+        failure_message_sent = 1;
+      }
+    } else {
+      response = 0;
+    }
+  }
+  return response;
+}
+
+int ptp_shm_interface_close() {
+  int response = -1;
+  if ((mapped_addr != MAP_FAILED) && (mapped_addr != NULL)) {
+    response = munmap(mapped_addr, sizeof(struct shm_structure));
+    if (response != 0)
+      debug(1, "error unmapping shared memory.");
+  }
+  mapped_addr = NULL;
+  return response;
+}
+
+void ptp_send_control_message_string(const char *msg) {
+  debug(2, "Send control message to NQPTP: \"%s\"", msg);
+  int s;
+  unsigned short port = htons(NQPTP_CONTROL_PORT);
+  struct sockaddr_in server;
+
+  /* Create a datagram socket in the internet domain and use the
+   * default protocol (UDP).
+   */
+  if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    die("Can't open a socket to NQPTP");
+  }
+
+  /* Set up the server name */
+  server.sin_family = AF_INET; /* Internet Domain    */
+  server.sin_port = port;      /* Server Port        */
+  server.sin_addr.s_addr = 0;  /* Server's Address   */
+
+  /* Send the message in buf to the server */
+  if (sendto(s, msg, (strlen(msg) + 1), 0, (struct sockaddr *)&server, sizeof(server)) < 0) {
+    die("error sending timing_peer_list to NQPTP");
+  }
+  /* Deallocate the socket */
+  close(s);
+}
diff --git a/ptp-utilities.h b/ptp-utilities.h
new file mode 100644 (file)
index 0000000..11a52c8
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Shairport Sync.
+ * Copyright (c) Mike Brady 2020 -- 2021
+ * All rights reserved.
+ *
+ * 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.
+ */
+#ifndef __PTP_UTILITIES_H
+#define __PTP_UTILITIES_H
+
+#include "config.h"
+#include <stdint.h>
+
+int ptp_get_clock_info(uint64_t *clock_id, uint64_t *offset);
+
+void ptp_send_control_message_string(const char *msg);
+
+int ptp_shm_interface_open();
+int ptp_shm_interface_close();
+
+#endif /* __PTP_UTILITIES_H */