├── LICENSE ├── README.md ├── srp.c ├── srp.h └── test_srp.c /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Tom Cocagne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | csrp 2 | ==== 3 | Tom Cocagne <tom.cocagne@gmail.com> 4 | 5 | csrp is a minimal C implementation of the [Secure Remote Password 6 | protocol](http://srp.stanford.edu/). The project consists of a single 7 | C file and is intended for direct inclusion into utilizing programs. 8 | It's only dependency is OpenSSL. 9 | 10 | *NOTE* This SRP implementation was created before the hashing algoritim specified 11 | in RFC 5054 became the de-facto standard for interoperable SRP implementations. 12 | The rfc5054_compat branch of this repository uses the RFC 5054 hashing algorithms 13 | and is known to be compatible with other SRP implementations. If this version works 14 | for you, please consider submitting a patch to this library that implements both the 15 | original default and the RFC 5054 implementation to allow a single mainline version 16 | of this library going forward. 17 | 18 | 19 | SRP Overview 20 | ------------ 21 | 22 | SRP is a cryptographically strong authentication 23 | protocol for password-based, mutual authentication over an insecure 24 | network connection. 25 | 26 | Unlike other common challenge-response autentication protocols, such 27 | as Kerberos and SSL, SRP does not rely on an external infrastructure 28 | of trusted key servers or certificate management. Instead, SRP server 29 | applications use verification keys derived from each user's password 30 | to determine the authenticity of a network connection. 31 | 32 | SRP provides mutual-authentication in that successful authentication 33 | requires both sides of the connection to have knowledge of the 34 | user's password. If the client side lacks the user's password or the 35 | server side lacks the proper verification key, the authentication will 36 | fail. 37 | 38 | Unlike SSL, SRP does not directly encrypt all data flowing through 39 | the authenticated connection. However, successful authentication does 40 | result in a cryptographically strong shared key that can be used 41 | for symmetric-key encryption. 42 | 43 | This library serves as the basis for a compatible Python module called 44 | [pysrp](https://github.com/cocagne/pysrp). The 45 | [pysrp](https://github.com/cocagne/pysrp) project contains complete, 46 | user-friendly API documentation as well as a comprehensive overview of the SRP 47 | protocol. As the APIs are virtually identical, the [pysrp 48 | documentation](http://pythonhosted.org/srp/) is an excellent reference for 49 | understanding this library. 50 | 51 | 52 | Usage Example 53 | ------------- 54 | 55 | ```c 56 | #include 57 | #include 58 | #include 59 | 60 | #include "srp.h" 61 | 62 | 63 | int main( int argc, char * argv[] ) 64 | { 65 | int auth_failed = 1; 66 | 67 | struct SRPVerifier * ver; 68 | struct SRPUser * usr; 69 | 70 | const unsigned char * bytes_s = 0; 71 | const unsigned char * bytes_v = 0; 72 | const unsigned char * bytes_A = 0; 73 | const unsigned char * bytes_B = 0; 74 | 75 | const unsigned char * bytes_M = 0; 76 | const unsigned char * bytes_HAMK = 0; 77 | 78 | int len_s = 0; 79 | int len_v = 0; 80 | int len_A = 0; 81 | int len_B = 0; 82 | int len_M = 0; 83 | 84 | const char * username = "testuser"; 85 | const char * password = "password"; 86 | 87 | const char * auth_username = 0; 88 | 89 | SRP_HashAlgorithm alg = SRP_SHA1; 90 | SRP_NGType ng_type = SRP_NG_2048; 91 | 92 | /* Create a salt+verification key for the user's password. The salt and 93 | * key need to be computed at the time the user's password is set and 94 | * must be stored by the server-side application for use during the 95 | * authentication process. 96 | */ 97 | srp_create_salted_verification_key( alg, ng_type, username, 98 | (const unsigned char *)password, 99 | strlen(password), 100 | &bytes_s, &len_s, 101 | &bytes_v, &len_v, 102 | NULL, NULL ); 103 | 104 | /* Begin authentication process */ 105 | usr = srp_user_new( alg, ng_type, username, 106 | (const unsigned char *)password, 107 | strlen(password), NULL, NULL ); 108 | 109 | srp_user_start_authentication( usr, &auth_username, &bytes_A, &len_A ); 110 | 111 | /* User -> Host: (username, bytes_A) */ 112 | ver = srp_verifier_new( alg, ng_type, username, bytes_s, len_s, bytes_v, len_v, 113 | bytes_A, len_A, & bytes_B, &len_B, NULL, NULL ); 114 | 115 | if ( !bytes_B ) { 116 | printf("Verifier SRP-6a safety check violated!\n"); 117 | goto auth_failed; 118 | } 119 | 120 | /* Host -> User: (bytes_s, bytes_B) */ 121 | srp_user_process_challenge( usr, bytes_s, len_s, bytes_B, len_B, &bytes_M, &len_M ); 122 | 123 | if ( !bytes_M ) { 124 | printf("User SRP-6a safety check violation!\n"); 125 | goto auth_failed; 126 | } 127 | 128 | /* User -> Host: (bytes_M) */ 129 | srp_verifier_verify_session( ver, bytes_M, &bytes_HAMK ); 130 | 131 | if ( !bytes_HAMK ) { 132 | printf("User authentication failed!\n"); 133 | goto auth_failed; 134 | } 135 | 136 | /* Host -> User: (HAMK) */ 137 | srp_user_verify_session( usr, bytes_HAMK ); 138 | 139 | if ( !srp_user_is_authenticated(usr) ) { 140 | printf("Server authentication failed!\n"); 141 | goto auth_failed; 142 | } 143 | 144 | auth_failed = 0; /* auth success! */ 145 | 146 | auth_failed: 147 | srp_verifier_delete( ver ); 148 | srp_user_delete( usr ); 149 | 150 | free( (char *)bytes_s ); 151 | free( (char *)bytes_v ); 152 | 153 | return auth_failed; 154 | } 155 | ``` 156 | -------------------------------------------------------------------------------- /srp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Secure Remote Password 6a implementation 3 | * Copyright (c) 2010 Tom Cocagne. All rights reserved. 4 | * https://github.com/cocagne/csrp 5 | * 6 | * The MIT License (MIT) 7 | * 8 | * Copyright (c) 2013 Tom Cocagne 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 11 | * this software and associated documentation files (the "Software"), to deal in 12 | * the Software without restriction, including without limitation the rights to 13 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is furnished to do 15 | * so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | */ 29 | 30 | #ifdef WIN32 31 | # include 32 | # include 33 | # ifdef X509_NAME 34 | # undef X509_NAME 35 | # endif 36 | #else 37 | #include 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | 50 | #include "srp.h" 51 | 52 | static int g_initialized = 0; 53 | 54 | typedef struct 55 | { 56 | BIGNUM * N; 57 | BIGNUM * g; 58 | } NGConstant; 59 | 60 | struct NGHex 61 | { 62 | const char * n_hex; 63 | const char * g_hex; 64 | }; 65 | 66 | /* All constants here were pulled from Appendix A of RFC 5054 */ 67 | static struct NGHex global_Ng_constants[] = { 68 | { /* 1024 */ 69 | "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496" 70 | "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E" 71 | "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA" 72 | "9AFD5138FE8376435B9FC61D2FC0EB06E3", 73 | "2" 74 | }, 75 | { /* 2048 */ 76 | "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4" 77 | "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60" 78 | "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF" 79 | "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907" 80 | "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861" 81 | "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB" 82 | "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", 83 | "2" 84 | }, 85 | { /* 4096 */ 86 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" 87 | "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" 88 | "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" 89 | "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" 90 | "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" 91 | "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" 92 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" 93 | "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" 94 | "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" 95 | "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" 96 | "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" 97 | "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" 98 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" 99 | "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" 100 | "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" 101 | "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" 102 | "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" 103 | "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" 104 | "FFFFFFFFFFFFFFFF", 105 | "5" 106 | }, 107 | { /* 8192 */ 108 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" 109 | "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" 110 | "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" 111 | "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" 112 | "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" 113 | "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" 114 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" 115 | "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" 116 | "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" 117 | "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" 118 | "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" 119 | "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" 120 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" 121 | "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" 122 | "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" 123 | "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" 124 | "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" 125 | "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" 126 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" 127 | "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" 128 | "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" 129 | "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" 130 | "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" 131 | "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" 132 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" 133 | "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" 134 | "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" 135 | "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" 136 | "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" 137 | "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" 138 | "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" 139 | "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" 140 | "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" 141 | "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" 142 | "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" 143 | "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" 144 | "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", 145 | "13" 146 | }, 147 | {0,0} /* null sentinel */ 148 | }; 149 | 150 | 151 | static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * g_hex ) 152 | { 153 | NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) ); 154 | ng->N = BN_new(); 155 | ng->g = BN_new(); 156 | 157 | if( !ng || !ng->N || !ng->g ) 158 | return 0; 159 | 160 | if ( ng_type != SRP_NG_CUSTOM ) 161 | { 162 | n_hex = global_Ng_constants[ ng_type ].n_hex; 163 | g_hex = global_Ng_constants[ ng_type ].g_hex; 164 | } 165 | 166 | BN_hex2bn( &ng->N, n_hex ); 167 | BN_hex2bn( &ng->g, g_hex ); 168 | 169 | return ng; 170 | } 171 | 172 | static void delete_ng( NGConstant * ng ) 173 | { 174 | if (ng) 175 | { 176 | BN_free( ng->N ); 177 | BN_free( ng->g ); 178 | ng->N = 0; 179 | ng->g = 0; 180 | free(ng); 181 | } 182 | } 183 | 184 | 185 | 186 | typedef union 187 | { 188 | SHA_CTX sha; 189 | SHA256_CTX sha256; 190 | SHA512_CTX sha512; 191 | } HashCTX; 192 | 193 | 194 | struct SRPVerifier 195 | { 196 | SRP_HashAlgorithm hash_alg; 197 | NGConstant *ng; 198 | 199 | const char * username; 200 | const unsigned char * bytes_B; 201 | int authenticated; 202 | 203 | unsigned char M [SHA512_DIGEST_LENGTH]; 204 | unsigned char H_AMK [SHA512_DIGEST_LENGTH]; 205 | unsigned char session_key [SHA512_DIGEST_LENGTH]; 206 | }; 207 | 208 | 209 | struct SRPUser 210 | { 211 | SRP_HashAlgorithm hash_alg; 212 | NGConstant *ng; 213 | 214 | BIGNUM *a; 215 | BIGNUM *A; 216 | BIGNUM *S; 217 | 218 | const unsigned char * bytes_A; 219 | int authenticated; 220 | 221 | const char * username; 222 | const unsigned char * password; 223 | int password_len; 224 | 225 | unsigned char M [SHA512_DIGEST_LENGTH]; 226 | unsigned char H_AMK [SHA512_DIGEST_LENGTH]; 227 | unsigned char session_key [SHA512_DIGEST_LENGTH]; 228 | }; 229 | 230 | 231 | static int hash_init( SRP_HashAlgorithm alg, HashCTX *c ) 232 | { 233 | switch (alg) 234 | { 235 | case SRP_SHA1 : return SHA1_Init( &c->sha ); 236 | case SRP_SHA224: return SHA224_Init( &c->sha256 ); 237 | case SRP_SHA256: return SHA256_Init( &c->sha256 ); 238 | case SRP_SHA384: return SHA384_Init( &c->sha512 ); 239 | case SRP_SHA512: return SHA512_Init( &c->sha512 ); 240 | default: 241 | return -1; 242 | }; 243 | } 244 | static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, size_t len ) 245 | { 246 | switch (alg) 247 | { 248 | case SRP_SHA1 : return SHA1_Update( &c->sha, data, len ); 249 | case SRP_SHA224: return SHA224_Update( &c->sha256, data, len ); 250 | case SRP_SHA256: return SHA256_Update( &c->sha256, data, len ); 251 | case SRP_SHA384: return SHA384_Update( &c->sha512, data, len ); 252 | case SRP_SHA512: return SHA512_Update( &c->sha512, data, len ); 253 | default: 254 | return -1; 255 | }; 256 | } 257 | static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md ) 258 | { 259 | switch (alg) 260 | { 261 | case SRP_SHA1 : return SHA1_Final( md, &c->sha ); 262 | case SRP_SHA224: return SHA224_Final( md, &c->sha256 ); 263 | case SRP_SHA256: return SHA256_Final( md, &c->sha256 ); 264 | case SRP_SHA384: return SHA384_Final( md, &c->sha512 ); 265 | case SRP_SHA512: return SHA512_Final( md, &c->sha512 ); 266 | default: 267 | return -1; 268 | }; 269 | } 270 | static unsigned char * hash( SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md ) 271 | { 272 | switch (alg) 273 | { 274 | case SRP_SHA1 : return SHA1( d, n, md ); 275 | case SRP_SHA224: return SHA224( d, n, md ); 276 | case SRP_SHA256: return SHA256( d, n, md ); 277 | case SRP_SHA384: return SHA384( d, n, md ); 278 | case SRP_SHA512: return SHA512( d, n, md ); 279 | default: 280 | return 0; 281 | }; 282 | } 283 | static int hash_length( SRP_HashAlgorithm alg ) 284 | { 285 | switch (alg) 286 | { 287 | case SRP_SHA1 : return SHA_DIGEST_LENGTH; 288 | case SRP_SHA224: return SHA224_DIGEST_LENGTH; 289 | case SRP_SHA256: return SHA256_DIGEST_LENGTH; 290 | case SRP_SHA384: return SHA384_DIGEST_LENGTH; 291 | case SRP_SHA512: return SHA512_DIGEST_LENGTH; 292 | default: 293 | return -1; 294 | }; 295 | } 296 | 297 | 298 | static BIGNUM * H_nn( SRP_HashAlgorithm alg, const BIGNUM * n1, const BIGNUM * n2 ) 299 | { 300 | unsigned char buff[ SHA512_DIGEST_LENGTH ]; 301 | int len_n1 = BN_num_bytes(n1); 302 | int len_n2 = BN_num_bytes(n2); 303 | int nbytes = len_n1 + len_n2; 304 | unsigned char * bin = (unsigned char *) malloc( nbytes ); 305 | if (!bin) 306 | return 0; 307 | BN_bn2bin(n1, bin); 308 | BN_bn2bin(n2, bin + len_n1); 309 | hash( alg, bin, nbytes, buff ); 310 | free(bin); 311 | return BN_bin2bn(buff, hash_length(alg), NULL); 312 | } 313 | 314 | static BIGNUM * H_ns( SRP_HashAlgorithm alg, const BIGNUM * n, const unsigned char * bytes, int len_bytes ) 315 | { 316 | unsigned char buff[ SHA512_DIGEST_LENGTH ]; 317 | int len_n = BN_num_bytes(n); 318 | int nbytes = len_n + len_bytes; 319 | unsigned char * bin = (unsigned char *) malloc( nbytes ); 320 | if (!bin) 321 | return 0; 322 | BN_bn2bin(n, bin); 323 | memcpy( bin + len_n, bytes, len_bytes ); 324 | hash( alg, bin, nbytes, buff ); 325 | free(bin); 326 | return BN_bin2bn(buff, hash_length(alg), NULL); 327 | } 328 | 329 | static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const char * username, const unsigned char * password, int password_len ) 330 | { 331 | unsigned char ucp_hash[SHA512_DIGEST_LENGTH]; 332 | HashCTX ctx; 333 | 334 | hash_init( alg, &ctx ); 335 | 336 | hash_update( alg, &ctx, username, strlen(username) ); 337 | hash_update( alg, &ctx, ":", 1 ); 338 | hash_update( alg, &ctx, password, password_len ); 339 | 340 | hash_final( alg, &ctx, ucp_hash ); 341 | 342 | return H_ns( alg, salt, ucp_hash, hash_length(alg) ); 343 | } 344 | 345 | static void update_hash_n( SRP_HashAlgorithm alg, HashCTX *ctx, const BIGNUM * n ) 346 | { 347 | unsigned long len = BN_num_bytes(n); 348 | unsigned char * n_bytes = (unsigned char *) malloc( len ); 349 | if (!n_bytes) 350 | return; 351 | BN_bn2bin(n, n_bytes); 352 | hash_update(alg, ctx, n_bytes, len); 353 | free(n_bytes); 354 | } 355 | 356 | static void hash_num( SRP_HashAlgorithm alg, const BIGNUM * n, unsigned char * dest ) 357 | { 358 | int nbytes = BN_num_bytes(n); 359 | unsigned char * bin = (unsigned char *) malloc( nbytes ); 360 | if(!bin) 361 | return; 362 | BN_bn2bin(n, bin); 363 | hash( alg, bin, nbytes, dest ); 364 | free(bin); 365 | } 366 | 367 | static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const BIGNUM * s, 368 | const BIGNUM * A, const BIGNUM * B, const unsigned char * K ) 369 | { 370 | unsigned char H_N[ SHA512_DIGEST_LENGTH ]; 371 | unsigned char H_g[ SHA512_DIGEST_LENGTH ]; 372 | unsigned char H_I[ SHA512_DIGEST_LENGTH ]; 373 | unsigned char H_xor[ SHA512_DIGEST_LENGTH ]; 374 | HashCTX ctx; 375 | int i = 0; 376 | int hash_len = hash_length(alg); 377 | 378 | hash_num( alg, ng->N, H_N ); 379 | hash_num( alg, ng->g, H_g ); 380 | 381 | hash(alg, (const unsigned char *)I, strlen(I), H_I); 382 | 383 | 384 | for (i=0; i < hash_len; i++ ) 385 | H_xor[i] = H_N[i] ^ H_g[i]; 386 | 387 | hash_init( alg, &ctx ); 388 | 389 | hash_update( alg, &ctx, H_xor, hash_len ); 390 | hash_update( alg, &ctx, H_I, hash_len ); 391 | update_hash_n( alg, &ctx, s ); 392 | update_hash_n( alg, &ctx, A ); 393 | update_hash_n( alg, &ctx, B ); 394 | hash_update( alg, &ctx, K, hash_len ); 395 | 396 | hash_final( alg, &ctx, dest ); 397 | } 398 | 399 | static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const BIGNUM * A, const unsigned char * M, const unsigned char * K ) 400 | { 401 | HashCTX ctx; 402 | 403 | hash_init( alg, &ctx ); 404 | 405 | update_hash_n( alg, &ctx, A ); 406 | hash_update( alg, &ctx, M, hash_length(alg) ); 407 | hash_update( alg, &ctx, K, hash_length(alg) ); 408 | 409 | hash_final( alg, &ctx, dest ); 410 | } 411 | 412 | 413 | static void init_random() 414 | { 415 | #ifdef WIN32 416 | HCRYPTPROV wctx; 417 | #else 418 | FILE *fp = 0; 419 | #endif 420 | 421 | unsigned char buff[64]; 422 | 423 | if (g_initialized) 424 | return; 425 | 426 | #ifdef WIN32 427 | 428 | CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 429 | 430 | CryptGenRandom(wctx, sizeof(buff), (BYTE*) buff); 431 | 432 | CryptReleaseContext(wctx, 0); 433 | 434 | g_initialized = 1; 435 | 436 | #else 437 | fp = fopen("/dev/urandom", "r"); 438 | 439 | if (fp) 440 | { 441 | size_t read = fread(buff, sizeof(buff), 1, fp); 442 | g_initialized = read == 1; 443 | fclose(fp); 444 | } 445 | #endif 446 | 447 | if (g_initialized) 448 | RAND_seed( buff, sizeof(buff) ); 449 | } 450 | 451 | 452 | /*********************************************************************************************************** 453 | * 454 | * Exported Functions 455 | * 456 | ***********************************************************************************************************/ 457 | 458 | void srp_random_seed( const unsigned char * random_data, int data_length ) 459 | { 460 | g_initialized = 1; 461 | 462 | if (random_data) 463 | RAND_seed( random_data, data_length ); 464 | } 465 | 466 | 467 | void srp_create_salted_verification_key( SRP_HashAlgorithm alg, 468 | SRP_NGType ng_type, const char * username, 469 | const unsigned char * password, int len_password, 470 | const unsigned char ** bytes_s, int * len_s, 471 | const unsigned char ** bytes_v, int * len_v, 472 | const char * n_hex, const char * g_hex ) 473 | { 474 | BIGNUM * s = BN_new(); 475 | BIGNUM * v = BN_new(); 476 | BIGNUM * x = 0; 477 | BN_CTX * ctx = BN_CTX_new(); 478 | NGConstant * ng = new_ng( ng_type, n_hex, g_hex ); 479 | 480 | if( !s || !v || !ctx || !ng ) 481 | goto cleanup_and_exit; 482 | 483 | init_random(); /* Only happens once */ 484 | 485 | BN_rand(s, 32, -1, 0); 486 | 487 | x = calculate_x( alg, s, username, password, len_password ); 488 | 489 | if( !x ) 490 | goto cleanup_and_exit; 491 | 492 | BN_mod_exp(v, ng->g, x, ng->N, ctx); 493 | 494 | *len_s = BN_num_bytes(s); 495 | *len_v = BN_num_bytes(v); 496 | 497 | *bytes_s = (const unsigned char *) malloc( *len_s ); 498 | *bytes_v = (const unsigned char *) malloc( *len_v ); 499 | 500 | if (!bytes_s || !bytes_v) 501 | goto cleanup_and_exit; 502 | 503 | BN_bn2bin(s, (unsigned char *) *bytes_s); 504 | BN_bn2bin(v, (unsigned char *) *bytes_v); 505 | 506 | cleanup_and_exit: 507 | delete_ng( ng ); 508 | BN_free(s); 509 | BN_free(v); 510 | BN_free(x); 511 | BN_CTX_free(ctx); 512 | } 513 | 514 | 515 | 516 | /* Out: bytes_B, len_B. 517 | * 518 | * On failure, bytes_B will be set to NULL and len_B will be set to 0 519 | */ 520 | struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, 521 | const unsigned char * bytes_s, int len_s, 522 | const unsigned char * bytes_v, int len_v, 523 | const unsigned char * bytes_A, int len_A, 524 | const unsigned char ** bytes_B, int * len_B, 525 | const char * n_hex, const char * g_hex ) 526 | { 527 | BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); 528 | BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL); 529 | BIGNUM *A = BN_bin2bn(bytes_A, len_A, NULL); 530 | BIGNUM *u = 0; 531 | BIGNUM *B = BN_new(); 532 | BIGNUM *S = BN_new(); 533 | BIGNUM *b = BN_new(); 534 | BIGNUM *k = 0; 535 | BIGNUM *tmp1 = BN_new(); 536 | BIGNUM *tmp2 = BN_new(); 537 | BN_CTX *ctx = BN_CTX_new(); 538 | int ulen = strlen(username) + 1; 539 | NGConstant *ng = new_ng( ng_type, n_hex, g_hex ); 540 | struct SRPVerifier *ver = 0; 541 | 542 | *len_B = 0; 543 | *bytes_B = 0; 544 | 545 | if( !s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ctx || !ng ) 546 | goto cleanup_and_exit; 547 | 548 | ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) ); 549 | 550 | if (!ver) 551 | goto cleanup_and_exit; 552 | 553 | init_random(); /* Only happens once */ 554 | 555 | ver->username = (char *) malloc( ulen ); 556 | ver->hash_alg = alg; 557 | ver->ng = ng; 558 | 559 | if (!ver->username) 560 | { 561 | free(ver); 562 | ver = 0; 563 | goto cleanup_and_exit; 564 | } 565 | 566 | memcpy( (char*)ver->username, username, ulen ); 567 | 568 | ver->authenticated = 0; 569 | 570 | /* SRP-6a safety check */ 571 | BN_mod(tmp1, A, ng->N, ctx); 572 | if ( !BN_is_zero(tmp1) ) 573 | { 574 | BN_rand(b, 256, -1, 0); 575 | 576 | k = H_nn(alg, ng->N, ng->g); 577 | 578 | /* B = kv + g^b */ 579 | BN_mul(tmp1, k, v, ctx); 580 | BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); 581 | BN_mod_add(B, tmp1, tmp2, ng->N, ctx); 582 | 583 | u = H_nn(alg, A, B); 584 | 585 | /* S = (A *(v^u)) ^ b */ 586 | BN_mod_exp(tmp1, v, u, ng->N, ctx); 587 | BN_mul(tmp2, A, tmp1, ctx); 588 | BN_mod_exp(S, tmp2, b, ng->N, ctx); 589 | 590 | hash_num(alg, S, ver->session_key); 591 | 592 | calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); 593 | calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); 594 | 595 | *len_B = BN_num_bytes(B); 596 | *bytes_B = (const unsigned char *) malloc( *len_B ); 597 | 598 | if( !*bytes_B ) 599 | { 600 | free( (void*) ver->username ); 601 | free( ver ); 602 | ver = 0; 603 | *len_B = 0; 604 | goto cleanup_and_exit; 605 | } 606 | 607 | BN_bn2bin( B, (unsigned char *) *bytes_B ); 608 | 609 | ver->bytes_B = *bytes_B; 610 | } 611 | 612 | cleanup_and_exit: 613 | BN_free(s); 614 | BN_free(v); 615 | BN_free(A); 616 | if (u) BN_free(u); 617 | if (k) BN_free(k); 618 | BN_free(B); 619 | BN_free(S); 620 | BN_free(b); 621 | BN_free(tmp1); 622 | BN_free(tmp2); 623 | BN_CTX_free(ctx); 624 | 625 | return ver; 626 | } 627 | 628 | 629 | 630 | 631 | void srp_verifier_delete( struct SRPVerifier * ver ) 632 | { 633 | if (ver) 634 | { 635 | delete_ng( ver->ng ); 636 | free( (char *) ver->username ); 637 | free( (unsigned char *) ver->bytes_B ); 638 | memset(ver, 0, sizeof(*ver)); 639 | free( ver ); 640 | } 641 | } 642 | 643 | 644 | 645 | int srp_verifier_is_authenticated( struct SRPVerifier * ver ) 646 | { 647 | return ver->authenticated; 648 | } 649 | 650 | 651 | const char * srp_verifier_get_username( struct SRPVerifier * ver ) 652 | { 653 | return ver->username; 654 | } 655 | 656 | 657 | const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ) 658 | { 659 | if (key_length) 660 | *key_length = hash_length( ver->hash_alg ); 661 | return ver->session_key; 662 | } 663 | 664 | 665 | int srp_verifier_get_session_key_length( struct SRPVerifier * ver ) 666 | { 667 | return hash_length( ver->hash_alg ); 668 | } 669 | 670 | 671 | /* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */ 672 | void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char * user_M, const unsigned char ** bytes_HAMK ) 673 | { 674 | if ( memcmp( ver->M, user_M, hash_length(ver->hash_alg) ) == 0 ) 675 | { 676 | ver->authenticated = 1; 677 | *bytes_HAMK = ver->H_AMK; 678 | } 679 | else 680 | *bytes_HAMK = NULL; 681 | } 682 | 683 | /*******************************************************************************/ 684 | 685 | struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, 686 | const unsigned char * bytes_password, int len_password, 687 | const char * n_hex, const char * g_hex ) 688 | { 689 | struct SRPUser *usr = (struct SRPUser *) malloc( sizeof(struct SRPUser) ); 690 | int ulen = strlen(username) + 1; 691 | 692 | if (!usr) 693 | goto err_exit; 694 | 695 | init_random(); /* Only happens once */ 696 | 697 | usr->hash_alg = alg; 698 | usr->ng = new_ng( ng_type, n_hex, g_hex ); 699 | 700 | usr->a = BN_new(); 701 | usr->A = BN_new(); 702 | usr->S = BN_new(); 703 | 704 | if (!usr->ng || !usr->a || !usr->A || !usr->S) 705 | goto err_exit; 706 | 707 | usr->username = (const char *) malloc(ulen); 708 | usr->password = (const unsigned char *) malloc(len_password); 709 | usr->password_len = len_password; 710 | 711 | if (!usr->username || !usr->password) 712 | goto err_exit; 713 | 714 | memcpy((char *)usr->username, username, ulen); 715 | memcpy((char *)usr->password, bytes_password, len_password); 716 | 717 | usr->authenticated = 0; 718 | 719 | usr->bytes_A = 0; 720 | 721 | return usr; 722 | 723 | err_exit: 724 | if (usr) 725 | { 726 | BN_free(usr->a); 727 | BN_free(usr->A); 728 | BN_free(usr->S); 729 | if (usr->username) 730 | free((void*)usr->username); 731 | if (usr->password) 732 | { 733 | memset((void*)usr->password, 0, usr->password_len); 734 | free((void*)usr->password); 735 | } 736 | free(usr); 737 | } 738 | 739 | return 0; 740 | } 741 | 742 | 743 | 744 | void srp_user_delete( struct SRPUser * usr ) 745 | { 746 | if( usr ) 747 | { 748 | BN_free( usr->a ); 749 | BN_free( usr->A ); 750 | BN_free( usr->S ); 751 | 752 | delete_ng( usr->ng ); 753 | 754 | memset((void*)usr->password, 0, usr->password_len); 755 | 756 | free((char *)usr->username); 757 | free((char *)usr->password); 758 | 759 | if (usr->bytes_A) 760 | free( (char *)usr->bytes_A ); 761 | 762 | memset(usr, 0, sizeof(*usr)); 763 | free( usr ); 764 | } 765 | } 766 | 767 | 768 | 769 | int srp_user_is_authenticated( struct SRPUser * usr) 770 | { 771 | return usr->authenticated; 772 | } 773 | 774 | 775 | const char * srp_user_get_username( struct SRPUser * usr ) 776 | { 777 | return usr->username; 778 | } 779 | 780 | 781 | 782 | const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length ) 783 | { 784 | if (key_length) 785 | *key_length = hash_length( usr->hash_alg ); 786 | return usr->session_key; 787 | } 788 | 789 | 790 | int srp_user_get_session_key_length( struct SRPUser * usr ) 791 | { 792 | return hash_length( usr->hash_alg ); 793 | } 794 | 795 | 796 | 797 | /* Output: username, bytes_A, len_A */ 798 | void srp_user_start_authentication( struct SRPUser * usr, const char ** username, 799 | const unsigned char ** bytes_A, int * len_A ) 800 | { 801 | BN_CTX *ctx = BN_CTX_new(); 802 | 803 | BN_rand(usr->a, 256, -1, 0); 804 | 805 | BN_mod_exp(usr->A, usr->ng->g, usr->a, usr->ng->N, ctx); 806 | 807 | BN_CTX_free(ctx); 808 | 809 | *len_A = BN_num_bytes(usr->A); 810 | *bytes_A = (const unsigned char *) malloc( *len_A ); 811 | 812 | if (!*bytes_A) 813 | { 814 | *len_A = 0; 815 | *bytes_A = 0; 816 | *username = 0; 817 | return; 818 | } 819 | 820 | BN_bn2bin( usr->A, (unsigned char *) *bytes_A ); 821 | 822 | usr->bytes_A = *bytes_A; 823 | *username = usr->username; 824 | } 825 | 826 | 827 | /* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ 828 | void srp_user_process_challenge( struct SRPUser * usr, 829 | const unsigned char * bytes_s, int len_s, 830 | const unsigned char * bytes_B, int len_B, 831 | const unsigned char ** bytes_M, int * len_M ) 832 | { 833 | BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); 834 | BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL); 835 | BIGNUM *u = 0; 836 | BIGNUM *x = 0; 837 | BIGNUM *k = 0; 838 | BIGNUM *v = BN_new(); 839 | BIGNUM *tmp1 = BN_new(); 840 | BIGNUM *tmp2 = BN_new(); 841 | BIGNUM *tmp3 = BN_new(); 842 | BN_CTX *ctx = BN_CTX_new(); 843 | 844 | *len_M = 0; 845 | *bytes_M = 0; 846 | 847 | if( !s || !B || !v || !tmp1 || !tmp2 || !tmp3 || !ctx ) 848 | goto cleanup_and_exit; 849 | 850 | u = H_nn(usr->hash_alg, usr->A, B); 851 | 852 | if (!u) 853 | goto cleanup_and_exit; 854 | 855 | x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len ); 856 | 857 | if (!x) 858 | goto cleanup_and_exit; 859 | 860 | k = H_nn(usr->hash_alg, usr->ng->N, usr->ng->g); 861 | 862 | if (!k) 863 | goto cleanup_and_exit; 864 | 865 | /* SRP-6a safety check */ 866 | if ( !BN_is_zero(B) && !BN_is_zero(u) ) 867 | { 868 | BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx); 869 | 870 | /* S = (B - k*(g^x)) ^ (a + ux) */ 871 | BN_mul(tmp1, u, x, ctx); 872 | BN_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ 873 | BN_mod_exp(tmp1, usr->ng->g, x, usr->ng->N, ctx); 874 | BN_mul(tmp3, k, tmp1, ctx); /* tmp3 = k*(g^x) */ 875 | BN_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */ 876 | BN_mod_exp(usr->S, tmp1, tmp2, usr->ng->N, ctx); 877 | 878 | hash_num(usr->hash_alg, usr->S, usr->session_key); 879 | 880 | calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key ); 881 | calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key ); 882 | 883 | *bytes_M = usr->M; 884 | if (len_M) 885 | *len_M = hash_length( usr->hash_alg ); 886 | } 887 | else 888 | { 889 | *bytes_M = NULL; 890 | if (len_M) 891 | *len_M = 0; 892 | } 893 | 894 | cleanup_and_exit: 895 | 896 | BN_free(s); 897 | BN_free(B); 898 | BN_free(u); 899 | BN_free(x); 900 | BN_free(k); 901 | BN_free(v); 902 | BN_free(tmp1); 903 | BN_free(tmp2); 904 | BN_free(tmp3); 905 | BN_CTX_free(ctx); 906 | } 907 | 908 | 909 | void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ) 910 | { 911 | if ( memcmp( usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg) ) == 0 ) 912 | usr->authenticated = 1; 913 | } 914 | -------------------------------------------------------------------------------- /srp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Secure Remote Password 6a implementation 3 | * Copyright (c) 2010 Tom Cocagne. All rights reserved. 4 | * https://github.com/cocagne/csrp 5 | * 6 | * The MIT License (MIT) 7 | * 8 | * Copyright (c) 2013 Tom Cocagne 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 11 | * this software and associated documentation files (the "Software"), to deal in 12 | * the Software without restriction, including without limitation the rights to 13 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 14 | * of the Software, and to permit persons to whom the Software is furnished to do 15 | * so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | * 28 | */ 29 | 30 | /* 31 | * 32 | * Purpose: This is a direct implementation of the Secure Remote Password 33 | * Protocol version 6a as described by 34 | * http://srp.stanford.edu/design.html 35 | * 36 | * Author: tom.cocagne@gmail.com (Tom Cocagne) 37 | * 38 | * Dependencies: OpenSSL (and Advapi32.lib on Windows) 39 | * 40 | * Usage: Refer to test_srp.c for a demonstration 41 | * 42 | * Notes: 43 | * This library allows multiple combinations of hashing algorithms and 44 | * prime number constants. For authentication to succeed, the hash and 45 | * prime number constants must match between 46 | * srp_create_salted_verification_key(), srp_user_new(), 47 | * and srp_verifier_new(). A recommended approach is to determine the 48 | * desired level of security for an application and globally define the 49 | * hash and prime number constants to the predetermined values. 50 | * 51 | * As one might suspect, more bits means more security. As one might also 52 | * suspect, more bits also means more processing time. The test_srp.c 53 | * program can be easily modified to profile various combinations of 54 | * hash & prime number pairings. 55 | */ 56 | 57 | #ifndef SRP_H 58 | #define SRP_H 59 | 60 | 61 | struct SRPVerifier; 62 | struct SRPUser; 63 | 64 | typedef enum 65 | { 66 | SRP_NG_1024, 67 | SRP_NG_2048, 68 | SRP_NG_4096, 69 | SRP_NG_8192, 70 | SRP_NG_CUSTOM 71 | } SRP_NGType; 72 | 73 | typedef enum 74 | { 75 | SRP_SHA1, 76 | SRP_SHA224, 77 | SRP_SHA256, 78 | SRP_SHA384, 79 | SRP_SHA512 80 | } SRP_HashAlgorithm; 81 | 82 | 83 | /* This library will automatically seed the OpenSSL random number generator 84 | * using cryptographically sound random data on Windows & Linux. If this is 85 | * undesirable behavior or the host OS does not provide a /dev/urandom file, 86 | * this function may be called to seed the random number generator with 87 | * alternate data. 88 | * 89 | * The random data should include at least as many bits of entropy as the 90 | * largest hash function used by the application. So, for example, if a 91 | * 512-bit hash function is used, the random data requies at least 512 92 | * bits of entropy. 93 | * 94 | * Passing a null pointer to this function will cause this library to skip 95 | * seeding the random number generator. This is only legitimate if it is 96 | * absolutely known that the OpenSSL random number generator has already 97 | * been sufficiently seeded within the running application. 98 | * 99 | * Notes: 100 | * * This function is optional on Windows & Linux and mandatory on all 101 | * other platforms. 102 | */ 103 | void srp_random_seed( const unsigned char * random_data, int data_length ); 104 | 105 | 106 | /* Out: bytes_s, len_s, bytes_v, len_v 107 | * 108 | * The caller is responsible for freeing the memory allocated for bytes_s and bytes_v 109 | * 110 | * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type. 111 | * If provided, they must contain ASCII text of the hexidecimal notation. 112 | */ 113 | void srp_create_salted_verification_key( SRP_HashAlgorithm alg, 114 | SRP_NGType ng_type, const char * username, 115 | const unsigned char * password, int len_password, 116 | const unsigned char ** bytes_s, int * len_s, 117 | const unsigned char ** bytes_v, int * len_v, 118 | const char * n_hex, const char * g_hex ); 119 | 120 | 121 | /* Out: bytes_B, len_B. 122 | * 123 | * On failure, bytes_B will be set to NULL and len_B will be set to 0 124 | * 125 | * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type 126 | */ 127 | struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, 128 | const unsigned char * bytes_s, int len_s, 129 | const unsigned char * bytes_v, int len_v, 130 | const unsigned char * bytes_A, int len_A, 131 | const unsigned char ** bytes_B, int * len_B, 132 | const char * n_hex, const char * g_hex ); 133 | 134 | 135 | void srp_verifier_delete( struct SRPVerifier * ver ); 136 | 137 | 138 | int srp_verifier_is_authenticated( struct SRPVerifier * ver ); 139 | 140 | 141 | const char * srp_verifier_get_username( struct SRPVerifier * ver ); 142 | 143 | /* key_length may be null */ 144 | const unsigned char * srp_verifier_get_session_key( struct SRPVerifier * ver, int * key_length ); 145 | 146 | 147 | int srp_verifier_get_session_key_length( struct SRPVerifier * ver ); 148 | 149 | 150 | /* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */ 151 | void srp_verifier_verify_session( struct SRPVerifier * ver, 152 | const unsigned char * user_M, 153 | const unsigned char ** bytes_HAMK ); 154 | 155 | /*******************************************************************************/ 156 | 157 | /* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */ 158 | struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, 159 | const unsigned char * bytes_password, int len_password, 160 | const char * n_hex, const char * g_hex ); 161 | 162 | void srp_user_delete( struct SRPUser * usr ); 163 | 164 | int srp_user_is_authenticated( struct SRPUser * usr); 165 | 166 | 167 | const char * srp_user_get_username( struct SRPUser * usr ); 168 | 169 | /* key_length may be null */ 170 | const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_length ); 171 | 172 | int srp_user_get_session_key_length( struct SRPUser * usr ); 173 | 174 | /* Output: username, bytes_A, len_A */ 175 | void srp_user_start_authentication( struct SRPUser * usr, const char ** username, 176 | const unsigned char ** bytes_A, int * len_A ); 177 | 178 | /* Output: bytes_M, len_M (len_M may be null and will always be 179 | * srp_user_get_session_key_length() bytes in size) */ 180 | void srp_user_process_challenge( struct SRPUser * usr, 181 | const unsigned char * bytes_s, int len_s, 182 | const unsigned char * bytes_B, int len_B, 183 | const unsigned char ** bytes_M, int * len_M ); 184 | 185 | /* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */ 186 | void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ); 187 | 188 | #endif /* Include Guard */ 189 | -------------------------------------------------------------------------------- /test_srp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #include "srp.h" 8 | 9 | 10 | #define NITER 100 11 | #define TEST_HASH SRP_SHA1 12 | #define TEST_NG SRP_NG_1024 13 | 14 | unsigned long long get_usec() 15 | { 16 | struct timeval t; 17 | gettimeofday(&t, NULL); 18 | return (((unsigned long long)t.tv_sec) * 1000000) + t.tv_usec; 19 | } 20 | 21 | const char * test_n_hex = "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496" 22 | "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E" 23 | "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA" 24 | "9AFD5138FE8376435B9FC61D2FC0EB06E3"; 25 | const char * test_g_hex = "2"; 26 | 27 | 28 | int main( int argc, char * argv[] ) 29 | { 30 | struct SRPVerifier * ver; 31 | struct SRPUser * usr; 32 | 33 | const unsigned char * bytes_s = 0; 34 | const unsigned char * bytes_v = 0; 35 | const unsigned char * bytes_A = 0; 36 | const unsigned char * bytes_B = 0; 37 | 38 | const unsigned char * bytes_M = 0; 39 | const unsigned char * bytes_HAMK = 0; 40 | 41 | int len_s = 0; 42 | int len_v = 0; 43 | int len_A = 0; 44 | int len_B = 0; 45 | int len_M = 0; 46 | int i; 47 | 48 | unsigned long long start; 49 | unsigned long long duration; 50 | 51 | const char * username = "testuser"; 52 | const char * password = "password"; 53 | 54 | const char * auth_username = 0; 55 | const char * n_hex = 0; 56 | const char * g_hex = 0; 57 | 58 | SRP_HashAlgorithm alg = TEST_HASH; 59 | SRP_NGType ng_type = SRP_NG_8192; //TEST_NG; 60 | 61 | if (ng_type == SRP_NG_CUSTOM) 62 | { 63 | n_hex = test_n_hex; 64 | g_hex = test_g_hex; 65 | } 66 | 67 | 68 | srp_create_salted_verification_key( alg, ng_type, username, 69 | (const unsigned char *)password, 70 | strlen(password), 71 | &bytes_s, &len_s, &bytes_v, &len_v, n_hex, g_hex ); 72 | 73 | 74 | 75 | start = get_usec(); 76 | 77 | for( i = 0; i < NITER; i++ ) 78 | { 79 | usr = srp_user_new( alg, ng_type, username, 80 | (const unsigned char *)password, 81 | strlen(password), n_hex, g_hex ); 82 | 83 | srp_user_start_authentication( usr, &auth_username, &bytes_A, &len_A ); 84 | 85 | /* User -> Host: (username, bytes_A) */ 86 | ver = srp_verifier_new( alg, ng_type, username, bytes_s, len_s, bytes_v, len_v, 87 | bytes_A, len_A, & bytes_B, &len_B, n_hex, g_hex ); 88 | 89 | if ( !bytes_B ) 90 | { 91 | printf("Verifier SRP-6a safety check violated!\n"); 92 | goto cleanup; 93 | } 94 | 95 | /* Host -> User: (bytes_s, bytes_B) */ 96 | srp_user_process_challenge( usr, bytes_s, len_s, bytes_B, len_B, &bytes_M, &len_M ); 97 | 98 | if ( !bytes_M ) 99 | { 100 | printf("User SRP-6a safety check violation!\n"); 101 | goto cleanup; 102 | } 103 | 104 | /* User -> Host: (bytes_M) */ 105 | srp_verifier_verify_session( ver, bytes_M, &bytes_HAMK ); 106 | 107 | if ( !bytes_HAMK ) 108 | { 109 | printf("User authentication failed!\n"); 110 | goto cleanup; 111 | } 112 | 113 | /* Host -> User: (HAMK) */ 114 | srp_user_verify_session( usr, bytes_HAMK ); 115 | 116 | if ( !srp_user_is_authenticated(usr) ) 117 | { 118 | printf("Server authentication failed!\n"); 119 | } 120 | 121 | cleanup: 122 | srp_verifier_delete( ver ); 123 | srp_user_delete( usr ); 124 | } 125 | 126 | duration = get_usec() - start; 127 | 128 | printf("Usec per call: %d\n", (int)(duration / NITER)); 129 | 130 | 131 | free( (char *)bytes_s ); 132 | free( (char *)bytes_v ); 133 | 134 | return 0; 135 | } 136 | --------------------------------------------------------------------------------