From 8c815b221458a300ca75b18d5ca44643a39a071d Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 12 Apr 2015 03:40:29 +0200 Subject: [PATCH] Port to LibGMP --- LICENSE | 5 +- README.md | 138 ++--------- sha/md32_common.h | 428 ++++++++++++++++++++++++++++++++ sha/sha2.h | 154 ++++++++++++ sha/sha256.c | 404 +++++++++++++++++++++++++++++++ sha/test_sha256.c | 60 +++++ srp.c | 606 +++++++++++++++++++++++----------------------- srp.h | 110 ++++----- test_srp.c | 4 +- 9 files changed, 1424 insertions(+), 485 deletions(-) create mode 100644 sha/md32_common.h create mode 100644 sha/sha2.h create mode 100644 sha/sha256.c create mode 100644 sha/test_sha256.c diff --git a/LICENSE b/LICENSE index 030645d..92c173f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,9 @@ +The files in this directory have the license stated below. +The license for the files inside the sha/ directory is included inside their boilerplate. + The MIT License (MIT) -Copyright (c) 2013 Tom Cocagne +Copyright (c) 2013 Tom Cocagne, 2015 est31 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 diff --git a/README.md b/README.md index c166215..2938d06 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -csrp -==== -Tom Cocagne <tom.cocagne@gmail.com> +csrp-gmp +======== -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. +csrp-gmp is a minimal C implementation of the [Secure Remote Password +protocol](http://srp.stanford.edu/), originally written by Tom Cocagne +to depend on OpenSSL, ported to LibGMP by est31. +The project consists of a single C file and is intended for direct +inclusion into utilizing programs. It's only dependency is LibGMP. SRP Overview ------------ @@ -31,115 +31,23 @@ 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. +Porter's notes +-------------- + +Compared with csrp, some things have changed for the outside. +As LibGMP doesn't ship with a cryptographically strong PRNG, strong +PRNGs provided (and seeded) by the OS are used instead. On unix-based +operating systems, you should ensure that /dev/urandom is readable. +The call `srp_random_seed` has been removed. + +The call `srp_user_new` has a new parameter, `username_for_verifier`, +allowing to use different usernames for verifier and srp login. + +We ship with OpenSSL's implementation of the SHA256 hash algorithm. +Support for other hash algoritms was dropped (but re-introducing is +fairly easy). Usage Example ------------- -```c -#include -#include -#include - -#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; -} -``` +For an usage example, see test_srp.c diff --git a/sha/md32_common.h b/sha/md32_common.h new file mode 100644 index 0000000..96828d2 --- /dev/null +++ b/sha/md32_common.h @@ -0,0 +1,428 @@ +/* crypto/md32_common.h */ +/* ==================================================================== + * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + */ + +/*- + * This is a generic 32 bit "collector" for message digest algorithms. + * Whenever needed it collects input character stream into chunks of + * 32 bit values and invokes a block function that performs actual hash + * calculations. + * + * Porting guide. + * + * Obligatory macros: + * + * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN + * this macro defines byte order of input stream. + * HASH_CBLOCK + * size of a unit chunk HASH_BLOCK operates on. + * HASH_LONG + * has to be at lest 32 bit wide, if it's wider, then + * HASH_LONG_LOG2 *has to* be defined along + * HASH_CTX + * context structure that at least contains following + * members: + * typedef struct { + * ... + * HASH_LONG Nl,Nh; + * either { + * HASH_LONG data[HASH_LBLOCK]; + * unsigned char data[HASH_CBLOCK]; + * }; + * unsigned int num; + * ... + * } HASH_CTX; + * data[] vector is expected to be zeroed upon first call to + * HASH_UPDATE. + * HASH_UPDATE + * name of "Update" function, implemented here. + * HASH_TRANSFORM + * name of "Transform" function, implemented here. + * HASH_FINAL + * name of "Final" function, implemented here. + * HASH_BLOCK_DATA_ORDER + * name of "block" function capable of treating *unaligned* input + * message in original (data) byte order, implemented externally. + * HASH_MAKE_STRING + * macro convering context variables to an ASCII hash string. + * + * MD5 example: + * + * #define DATA_ORDER_IS_LITTLE_ENDIAN + * + * #define HASH_LONG MD5_LONG + * #define HASH_LONG_LOG2 MD5_LONG_LOG2 + * #define HASH_CTX MD5_CTX + * #define HASH_CBLOCK MD5_CBLOCK + * #define HASH_UPDATE MD5_Update + * #define HASH_TRANSFORM MD5_Transform + * #define HASH_FINAL MD5_Final + * #define HASH_BLOCK_DATA_ORDER md5_block_data_order + * + * + */ + +#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# error "DATA_ORDER must be defined!" +#endif + +#ifndef HASH_CBLOCK +# error "HASH_CBLOCK must be defined!" +#endif +#ifndef HASH_LONG +# error "HASH_LONG must be defined!" +#endif +#ifndef HASH_CTX +# error "HASH_CTX must be defined!" +#endif + +#ifndef HASH_UPDATE +# error "HASH_UPDATE must be defined!" +#endif +#ifndef HASH_TRANSFORM +# error "HASH_TRANSFORM must be defined!" +#endif +#ifndef HASH_FINAL +# error "HASH_FINAL must be defined!" +#endif + +#ifndef HASH_BLOCK_DATA_ORDER +# error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif + +/* + * Engage compiler specific rotate intrinsic function if available. + */ +#undef ROTATE +#ifndef PEDANTIC +# if defined(_MSC_VER) +# define ROTATE(a,n) _lrotl(a,n) +# elif defined(__ICC) +# define ROTATE(a,n) _rotl(a,n) +# elif defined(__MWERKS__) +# if defined(__POWERPC__) +# define ROTATE(a,n) __rlwinm(a,n,0,31) +# elif defined(__MC68K__) + /* Motorola specific tweak. */ +# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) +# else +# define ROTATE(a,n) __rol(a,n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + */ +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r"(ret) \ + : "I"(n), "0"((unsigned int)(a)) \ + : "cc"); \ + ret; \ + }) +# elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ + defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# elif defined(__s390x__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ("rll %0,%1,%2" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# endif +# endif +#endif /* PEDANTIC */ + +#ifndef ROTATE +# define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + +# ifndef PEDANTIC +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +# if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \ + (defined(__x86_64) || defined(__x86_64__)) +# if !defined(B_ENDIAN) + /* + * This gives ~30-40% performance improvement in SHA-256 compiled + * with gcc [on P4]. Well, first macro to be frank. We can pull + * this trick on x86* platforms only, because these CPUs can fetch + * unaligned data without raising an exception. + */ +# define HOST_c2l(c,l) ({ unsigned int r=*((const unsigned int *)(c)); \ + asm ("bswapl %0":"=r"(r):"0"(r)); \ + (c)+=4; (l)=r; }) +# define HOST_l2c(l,c) ({ unsigned int r=(l); \ + asm ("bswapl %0":"=r"(r):"0"(r)); \ + *((unsigned int *)(c))=r; (c)+=4; r; }) +# endif +# elif defined(__aarch64__) +# if defined(__BYTE_ORDER__) +# if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ +# define HOST_c2l(c,l) ({ unsigned int r; \ + asm ("rev %w0,%w1" \ + :"=r"(r) \ + :"r"(*((const unsigned int *)(c))));\ + (c)+=4; (l)=r; }) +# define HOST_l2c(l,c) ({ unsigned int r; \ + asm ("rev %w0,%w1" \ + :"=r"(r) \ + :"r"((unsigned int)(l)));\ + *((unsigned int *)(c))=r; (c)+=4; r; }) +# elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l)) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l)) +# endif +# endif +# endif +# endif +# if defined(__s390__) || defined(__s390x__) +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l)) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l)) +# endif +# endif + +# ifndef HOST_c2l +# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ) ) +# endif +# ifndef HOST_l2c +# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) +# endif + +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + +# ifndef PEDANTIC +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +# if defined(__s390x__) +# define HOST_c2l(c,l) ({ asm ("lrv %0,%1" \ + :"=d"(l) :"m"(*(const unsigned int *)(c)));\ + (c)+=4; (l); }) +# define HOST_l2c(l,c) ({ asm ("strv %1,%0" \ + :"=m"(*(unsigned int *)(c)) :"d"(l));\ + (c)+=4; (l); }) +# endif +# endif +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# ifndef B_ENDIAN + /* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */ +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, l) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, l) +# endif +# endif +# endif + +# ifndef HOST_c2l +# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24) ) +# endif +# ifndef HOST_l2c +# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) +# endif + +#endif + +/* + * Time for some action:-) + */ + +int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len) +{ + const unsigned char *data = data_; + unsigned char *p; + HASH_LONG l; + size_t n; + + if (len == 0) + return 1; + + l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL; + /* + * 95-05-24 eay Fixed a bug with the overflow handling, thanks to Wei Dai + * for pointing it out. + */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on + * 16-bit */ + c->Nl = l; + + n = c->num; + if (n != 0) { + p = (unsigned char *)c->data; + + if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) { + memcpy(p + n, data, HASH_CBLOCK - n); + HASH_BLOCK_DATA_ORDER(c, p, 1); + n = HASH_CBLOCK - n; + data += n; + len -= n; + c->num = 0; + memset(p, 0, HASH_CBLOCK); /* keep it zeroed */ + } else { + memcpy(p + n, data, len); + c->num += (unsigned int)len; + return 1; + } + } + + n = len / HASH_CBLOCK; + if (n > 0) { + HASH_BLOCK_DATA_ORDER(c, data, n); + n *= HASH_CBLOCK; + data += n; + len -= n; + } + + if (len != 0) { + p = (unsigned char *)c->data; + c->num = (unsigned int)len; + memcpy(p, data, len); + } + return 1; +} + +void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data) +{ + HASH_BLOCK_DATA_ORDER(c, data, 1); +} + +int HASH_FINAL(unsigned char *md, HASH_CTX *c) +{ + unsigned char *p = (unsigned char *)c->data; + size_t n = c->num; + + p[n] = 0x80; /* there is always room for one */ + n++; + + if (n > (HASH_CBLOCK - 8)) { + memset(p + n, 0, HASH_CBLOCK - n); + n = 0; + HASH_BLOCK_DATA_ORDER(c, p, 1); + } + memset(p + n, 0, HASH_CBLOCK - 8 - n); + + p += HASH_CBLOCK - 8; +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + (void)HOST_l2c(c->Nh, p); + (void)HOST_l2c(c->Nl, p); +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + (void)HOST_l2c(c->Nl, p); + (void)HOST_l2c(c->Nh, p); +#endif + p -= HASH_CBLOCK; + HASH_BLOCK_DATA_ORDER(c, p, 1); + c->num = 0; + memset(p, 0, HASH_CBLOCK); + +#ifndef HASH_MAKE_STRING +# error "HASH_MAKE_STRING must be defined!" +#else + HASH_MAKE_STRING(c, md); +#endif + + return 1; +} + +#ifndef MD32_REG_T +# if defined(__alpha) || defined(__sparcv9) || defined(__mips) +# define MD32_REG_T long +/* + * This comment was originaly written for MD5, which is why it + * discusses A-D. But it basically applies to all 32-bit digests, + * which is why it was moved to common header file. + * + * In case you wonder why A-D are declared as long and not + * as MD5_LONG. Doing so results in slight performance + * boost on LP64 architectures. The catch is we don't + * really care if 32 MSBs of a 64-bit register get polluted + * with eventual overflows as we *save* only 32 LSBs in + * *either* case. Now declaring 'em long excuses the compiler + * from keeping 32 MSBs zeroed resulting in 13% performance + * improvement under SPARC Solaris7/64 and 5% under AlphaLinux. + * Well, to be honest it should say that this *prevents* + * performance degradation. + * + */ +# else +/* + * Above is not absolute and there are LP64 compilers that + * generate better code if MD32_REG_T is defined int. The above + * pre-processor condition reflects the circumstances under which + * the conclusion was made and is subject to further extension. + * + */ +# define MD32_REG_T int +# endif +#endif diff --git a/sha/sha2.h b/sha/sha2.h new file mode 100644 index 0000000..6ac045f --- /dev/null +++ b/sha/sha2.h @@ -0,0 +1,154 @@ +/* crypto/sha/sha.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_SHA_H +# define HEADER_SHA_H + +# include + +#ifdef __cplusplus +extern "C" { +#endif + +# if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1)) +# error SHA is disabled. +# endif + +# if defined(OPENSSL_FIPS) +# define FIPS_SHA_SIZE_T size_t +# endif + +/* + Compat stuff from OpenSSL land + */ + +/* crypto.h */ + +# define fips_md_init(alg) fips_md_init_ctx(alg, alg) + +# define fips_md_init_ctx(alg, cx) \ + int alg##_Init(cx##_CTX *c) +# define fips_cipher_abort(alg) while(0) + +/*- + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! SHA_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! SHA_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +# if defined(__LP32__) +# define SHA_LONG unsigned long +# elif defined(__ILP64__) +# define SHA_LONG unsigned long +# define SHA_LONG_LOG2 3 +# else +# define SHA_LONG unsigned int +# endif + +# define SHA_LBLOCK 16 +# define SHA_CBLOCK (SHA_LBLOCK*4)/* SHA treats input data as a + * contiguous array of 32 bit wide + * big-endian values. */ +# define SHA_LAST_BLOCK (SHA_CBLOCK-8) +# define SHA_DIGEST_LENGTH 20 + +typedef struct SHAstate_st { + SHA_LONG h0, h1, h2, h3, h4; + SHA_LONG Nl, Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num; +} SHA_CTX; + +# define SHA256_CBLOCK (SHA_LBLOCK*4)/* SHA-256 treats input data as a + * contiguous array of 32 bit wide + * big-endian values. */ +# define SHA224_DIGEST_LENGTH 28 +# define SHA256_DIGEST_LENGTH 32 + +typedef struct SHA256state_st { + SHA_LONG h[8]; + SHA_LONG Nl, Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num, md_len; +} SHA256_CTX; + +# ifndef OPENSSL_NO_SHA256 +# ifdef OPENSSL_FIPS +int private_SHA224_Init(SHA256_CTX *c); +int private_SHA256_Init(SHA256_CTX *c); +# endif +int SHA224_Init(SHA256_CTX *c); +int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); +int SHA224_Final(unsigned char *md, SHA256_CTX *c); +unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md); +int SHA256_Init(SHA256_CTX *c); +int SHA256_Update(SHA256_CTX *c, const void *data, size_t len); +int SHA256_Final(unsigned char *md, SHA256_CTX *c); +unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); +void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); +# endif + +# define SHA384_DIGEST_LENGTH 48 +# define SHA512_DIGEST_LENGTH 64 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sha/sha256.c b/sha/sha256.c new file mode 100644 index 0000000..a1491b3 --- /dev/null +++ b/sha/sha256.c @@ -0,0 +1,404 @@ +/* crypto/sha/sha256.c */ +/* ==================================================================== + * Copyright (c) 2004 The OpenSSL Project. All rights reserved + * according to the OpenSSL license [found in ../../LICENSE]. + * ==================================================================== + */ +# include +# include + +# include "sha2.h" + +# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2a 19 Mar 2015" +# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT + +const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT; + +/* mem_clr.c */ +unsigned char cleanse_ctr = 0; +void OPENSSL_cleanse(void *ptr, size_t len) +{ + unsigned char *p = ptr; + size_t loop = len, ctr = cleanse_ctr; + while (loop--) { + *(p++) = (unsigned char)ctr; + ctr += (17 + ((size_t)p & 0xF)); + } + p = memchr(ptr, (unsigned char)ctr, len); + if (p) + ctr += (63 + (size_t)p); + cleanse_ctr = (unsigned char)ctr; +} + +# define fips_md_init(alg) fips_md_init_ctx(alg, alg) +# define fips_md_init_ctx(alg, cx) \ + int alg##_Init(cx##_CTX *c) +# define fips_cipher_abort(alg) while(0) + +fips_md_init_ctx(SHA224, SHA256) +{ + memset(c, 0, sizeof(*c)); + c->h[0] = 0xc1059ed8UL; + c->h[1] = 0x367cd507UL; + c->h[2] = 0x3070dd17UL; + c->h[3] = 0xf70e5939UL; + c->h[4] = 0xffc00b31UL; + c->h[5] = 0x68581511UL; + c->h[6] = 0x64f98fa7UL; + c->h[7] = 0xbefa4fa4UL; + c->md_len = SHA224_DIGEST_LENGTH; + return 1; +} + +fips_md_init(SHA256) +{ + memset(c, 0, sizeof(*c)); + c->h[0] = 0x6a09e667UL; + c->h[1] = 0xbb67ae85UL; + c->h[2] = 0x3c6ef372UL; + c->h[3] = 0xa54ff53aUL; + c->h[4] = 0x510e527fUL; + c->h[5] = 0x9b05688cUL; + c->h[6] = 0x1f83d9abUL; + c->h[7] = 0x5be0cd19UL; + c->md_len = SHA256_DIGEST_LENGTH; + return 1; +} + +unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md) +{ + SHA256_CTX c; + static unsigned char m[SHA224_DIGEST_LENGTH]; + + if (md == NULL) + md = m; + SHA224_Init(&c); + SHA256_Update(&c, d, n); + SHA256_Final(md, &c); + OPENSSL_cleanse(&c, sizeof(c)); + return (md); +} + +unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md) +{ + SHA256_CTX c; + static unsigned char m[SHA256_DIGEST_LENGTH]; + + if (md == NULL) + md = m; + SHA256_Init(&c); + SHA256_Update(&c, d, n); + SHA256_Final(md, &c); + OPENSSL_cleanse(&c, sizeof(c)); + return (md); +} + +int SHA224_Update(SHA256_CTX *c, const void *data, size_t len) +{ + return SHA256_Update(c, data, len); +} + +int SHA224_Final(unsigned char *md, SHA256_CTX *c) +{ + return SHA256_Final(md, c); +} + +# define DATA_ORDER_IS_BIG_ENDIAN + +# define HASH_LONG SHA_LONG +# define HASH_CTX SHA256_CTX +# define HASH_CBLOCK SHA_CBLOCK +/* + * Note that FIPS180-2 discusses "Truncation of the Hash Function Output." + * default: case below covers for it. It's not clear however if it's + * permitted to truncate to amount of bytes not divisible by 4. I bet not, + * but if it is, then default: case shall be extended. For reference. + * Idea behind separate cases for pre-defined lenghts is to let the + * compiler decide if it's appropriate to unroll small loops. + */ +# define HASH_MAKE_STRING(c,s) do { \ + unsigned long ll; \ + unsigned int nn; \ + switch ((c)->md_len) \ + { case SHA224_DIGEST_LENGTH: \ + for (nn=0;nnh[nn]; (void)HOST_l2c(ll,(s)); } \ + break; \ + case SHA256_DIGEST_LENGTH: \ + for (nn=0;nnh[nn]; (void)HOST_l2c(ll,(s)); } \ + break; \ + default: \ + if ((c)->md_len > SHA256_DIGEST_LENGTH) \ + return 0; \ + for (nn=0;nn<(c)->md_len/4;nn++) \ + { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \ + break; \ + } \ + } while (0) + +# define HASH_UPDATE SHA256_Update +# define HASH_TRANSFORM SHA256_Transform +# define HASH_FINAL SHA256_Final +# define HASH_BLOCK_DATA_ORDER sha256_block_data_order +# ifndef SHA256_ASM +static +# endif +void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num); + +# include "md32_common.h" + +# ifndef SHA256_ASM +static const SHA_LONG K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* + * FIPS specification refers to right rotations, while our ROTATE macro + * is left one. This is why you might notice that rotation coefficients + * differ from those observed in FIPS document by 32-N... + */ +# define Sigma0(x) (ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10)) +# define Sigma1(x) (ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7)) +# define sigma0(x) (ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3)) +# define sigma1(x) (ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10)) + +# define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +# define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +# ifdef OPENSSL_SMALL_FOOTPRINT + +static void sha256_block_data_order(SHA256_CTX *ctx, const void *in, + size_t num) +{ + unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2; + SHA_LONG X[16], l; + int i; + const unsigned char *data = in; + + while (num--) { + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + f = ctx->h[5]; + g = ctx->h[6]; + h = ctx->h[7]; + + for (i = 0; i < 16; i++) { + HOST_c2l(data, l); + T1 = X[i] = l; + T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i]; + T2 = Sigma0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + for (; i < 64; i++) { + s0 = X[(i + 1) & 0x0f]; + s0 = sigma0(s0); + s1 = X[(i + 14) & 0x0f]; + s1 = sigma1(s1); + + T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf]; + T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i]; + T2 = Sigma0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; + ctx->h[5] += f; + ctx->h[6] += g; + ctx->h[7] += h; + + } +} + +# else + +# define ROUND_00_15(i,a,b,c,d,e,f,g,h) do { \ + T1 += h + Sigma1(e) + Ch(e,f,g) + K256[i]; \ + h = Sigma0(a) + Maj(a,b,c); \ + d += T1; h += T1; } while (0) + +# define ROUND_16_63(i,a,b,c,d,e,f,g,h,X) do { \ + s0 = X[(i+1)&0x0f]; s0 = sigma0(s0); \ + s1 = X[(i+14)&0x0f]; s1 = sigma1(s1); \ + T1 = X[(i)&0x0f] += s0 + s1 + X[(i+9)&0x0f]; \ + ROUND_00_15(i,a,b,c,d,e,f,g,h); } while (0) + +static void sha256_block_data_order(SHA256_CTX *ctx, const void *in, + size_t num) +{ + unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1; + SHA_LONG X[16]; + int i; + const unsigned char *data = in; + const union { + long one; + char little; + } is_endian = { + 1 + }; + + while (num--) { + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + f = ctx->h[5]; + g = ctx->h[6]; + h = ctx->h[7]; + + if (!is_endian.little && sizeof(SHA_LONG) == 4 + && ((size_t)in % 4) == 0) { + const SHA_LONG *W = (const SHA_LONG *)data; + + T1 = X[0] = W[0]; + ROUND_00_15(0, a, b, c, d, e, f, g, h); + T1 = X[1] = W[1]; + ROUND_00_15(1, h, a, b, c, d, e, f, g); + T1 = X[2] = W[2]; + ROUND_00_15(2, g, h, a, b, c, d, e, f); + T1 = X[3] = W[3]; + ROUND_00_15(3, f, g, h, a, b, c, d, e); + T1 = X[4] = W[4]; + ROUND_00_15(4, e, f, g, h, a, b, c, d); + T1 = X[5] = W[5]; + ROUND_00_15(5, d, e, f, g, h, a, b, c); + T1 = X[6] = W[6]; + ROUND_00_15(6, c, d, e, f, g, h, a, b); + T1 = X[7] = W[7]; + ROUND_00_15(7, b, c, d, e, f, g, h, a); + T1 = X[8] = W[8]; + ROUND_00_15(8, a, b, c, d, e, f, g, h); + T1 = X[9] = W[9]; + ROUND_00_15(9, h, a, b, c, d, e, f, g); + T1 = X[10] = W[10]; + ROUND_00_15(10, g, h, a, b, c, d, e, f); + T1 = X[11] = W[11]; + ROUND_00_15(11, f, g, h, a, b, c, d, e); + T1 = X[12] = W[12]; + ROUND_00_15(12, e, f, g, h, a, b, c, d); + T1 = X[13] = W[13]; + ROUND_00_15(13, d, e, f, g, h, a, b, c); + T1 = X[14] = W[14]; + ROUND_00_15(14, c, d, e, f, g, h, a, b); + T1 = X[15] = W[15]; + ROUND_00_15(15, b, c, d, e, f, g, h, a); + + data += SHA256_CBLOCK; + } else { + SHA_LONG l; + + HOST_c2l(data, l); + T1 = X[0] = l; + ROUND_00_15(0, a, b, c, d, e, f, g, h); + HOST_c2l(data, l); + T1 = X[1] = l; + ROUND_00_15(1, h, a, b, c, d, e, f, g); + HOST_c2l(data, l); + T1 = X[2] = l; + ROUND_00_15(2, g, h, a, b, c, d, e, f); + HOST_c2l(data, l); + T1 = X[3] = l; + ROUND_00_15(3, f, g, h, a, b, c, d, e); + HOST_c2l(data, l); + T1 = X[4] = l; + ROUND_00_15(4, e, f, g, h, a, b, c, d); + HOST_c2l(data, l); + T1 = X[5] = l; + ROUND_00_15(5, d, e, f, g, h, a, b, c); + HOST_c2l(data, l); + T1 = X[6] = l; + ROUND_00_15(6, c, d, e, f, g, h, a, b); + HOST_c2l(data, l); + T1 = X[7] = l; + ROUND_00_15(7, b, c, d, e, f, g, h, a); + HOST_c2l(data, l); + T1 = X[8] = l; + ROUND_00_15(8, a, b, c, d, e, f, g, h); + HOST_c2l(data, l); + T1 = X[9] = l; + ROUND_00_15(9, h, a, b, c, d, e, f, g); + HOST_c2l(data, l); + T1 = X[10] = l; + ROUND_00_15(10, g, h, a, b, c, d, e, f); + HOST_c2l(data, l); + T1 = X[11] = l; + ROUND_00_15(11, f, g, h, a, b, c, d, e); + HOST_c2l(data, l); + T1 = X[12] = l; + ROUND_00_15(12, e, f, g, h, a, b, c, d); + HOST_c2l(data, l); + T1 = X[13] = l; + ROUND_00_15(13, d, e, f, g, h, a, b, c); + HOST_c2l(data, l); + T1 = X[14] = l; + ROUND_00_15(14, c, d, e, f, g, h, a, b); + HOST_c2l(data, l); + T1 = X[15] = l; + ROUND_00_15(15, b, c, d, e, f, g, h, a); + } + + for (i = 16; i < 64; i += 8) { + ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X); + ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X); + ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X); + ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X); + ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X); + ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X); + ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X); + ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X); + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; + ctx->h[5] += f; + ctx->h[6] += g; + ctx->h[7] += h; + + } +} + +# endif +# endif /* SHA256_ASM */ diff --git a/sha/test_sha256.c b/sha/test_sha256.c new file mode 100644 index 0000000..cd74a00 --- /dev/null +++ b/sha/test_sha256.c @@ -0,0 +1,60 @@ +#include +#include +#include + +unsigned char app_b1[SHA256_DIGEST_LENGTH] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad +}; + +unsigned char app_b2[SHA256_DIGEST_LENGTH] = { + 0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0 +}; + +int main(int argc, char **argv) +{ + unsigned char md[SHA256_DIGEST_LENGTH]; + + fprintf(stdout, "Testing SHA-256 "); + SHA256_CTX *state = malloc(sizeof(SHA256_CTX)); + SHA256_Init(state); + + SHA256_Update(state, "abc", 3); + SHA256_Final(md, state); + if (memcmp(md, app_b1, sizeof(app_b1))) { + fflush(stdout); + fprintf(stderr, "\nTEST 1 of 2 failed.\n"); + return 1; + } else + fprintf(stdout, "."); + fflush(stdout); + + SHA256_Init(state); + int i; + for (i = 0; i < 1000000; i += 160) + SHA256_Update(state, "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" + "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" + "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" + "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" + "aaaaaaaa" "aaaaaaaa" "aaaaaaaa" "aaaaaaaa", + (1000000 - i) < 160 ? 1000000 - i : 160); + SHA256_Final(md, state); + + if (memcmp(md, app_b2, sizeof(app_b2))) { + fflush(stdout); + fprintf(stderr, "\nTEST 2 of 2 failed.\n"); + return 1; + } else + fprintf(stdout, "."); + + free(state); + fprintf(stdout, " passed.\n"); + fflush(stdout); + + return 0; +} diff --git a/srp.c b/srp.c index e49b96a..6152d3c 100644 --- a/srp.c +++ b/srp.c @@ -4,19 +4,19 @@ * https://github.com/cocagne/csrp * * The MIT License (MIT) - * - * Copyright (c) 2013 Tom Cocagne - * + * + * Copyright (c) 2013 Tom Cocagne, 2015 est31 + * * 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 @@ -24,7 +24,7 @@ * 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 @@ -37,23 +37,24 @@ #include #include -#include -#include -#include -#include - +#include +#include #include "srp.h" static int g_initialized = 0; +#define RAND_BUFF_MAX 128 +static unsigned int g_rand_idx; +static unsigned char g_rand_buff[RAND_BUFF_MAX]; + typedef struct { - BIGNUM * N; - BIGNUM * g; + mpz_t N; + mpz_t g; } NGConstant; -struct NGHex +struct NGHex { const char * n_hex; const char * g_hex; @@ -146,9 +147,9 @@ static struct NGHex global_Ng_constants[] = { 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(); + NGConstant * ng = (NGConstant *) malloc( sizeof(NGConstant) ); + mpz_init(ng->N); + mpz_init(ng->g); if( !ng || !ng->N || !ng->g ) return 0; @@ -158,10 +159,10 @@ static NGConstant * new_ng( SRP_NGType ng_type, const char * n_hex, const char * 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 ); - + + mpz_set_str( ng->N, n_hex, 16 ); + mpz_set_str( ng->g, g_hex, 16 ); + return ng; } @@ -169,21 +170,19 @@ static void delete_ng( NGConstant * ng ) { if (ng) { - BN_free( ng->N ); - BN_free( ng->g ); - ng->N = 0; - ng->g = 0; + mpz_clear( ng->N ); + mpz_clear( ng->g ); free(ng); } } -typedef union +typedef union { SHA_CTX sha; SHA256_CTX sha256; - SHA512_CTX sha512; + //SHA512_CTX sha512; } HashCTX; @@ -191,11 +190,11 @@ 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]; @@ -206,18 +205,19 @@ struct SRPUser { SRP_HashAlgorithm hash_alg; NGConstant *ng; - - BIGNUM *a; - BIGNUM *A; - BIGNUM *S; + + mpz_t a; + mpz_t A; + mpz_t S; const unsigned char * bytes_A; int authenticated; - + const char * username; + const char * username_verifier; 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]; @@ -228,11 +228,11 @@ 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_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 ); + /*case SRP_SHA384: return SHA384_Init( &c->sha512 ); + case SRP_SHA512: return SHA512_Init( &c->sha512 );*/ default: return -1; }; @@ -241,11 +241,11 @@ static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, siz { switch (alg) { - case SRP_SHA1 : return SHA1_Update( &c->sha, data, len ); - case SRP_SHA224: return SHA224_Update( &c->sha256, data, len ); + /*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 ); + /*case SRP_SHA384: return SHA384_Update( &c->sha512, data, len ); + case SRP_SHA512: return SHA512_Update( &c->sha512, data, len );*/ default: return -1; }; @@ -254,11 +254,11 @@ 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_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 ); + /*case SRP_SHA384: return SHA384_Final( md, &c->sha512 ); + case SRP_SHA512: return SHA512_Final( md, &c->sha512 );*/ default: return -1; }; @@ -267,11 +267,11 @@ static unsigned char * hash( SRP_HashAlgorithm alg, const unsigned char *d, size { switch (alg) { - case SRP_SHA1 : return SHA1( d, n, md ); - case SRP_SHA224: return SHA224( d, n, md ); + /*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 ); + /*case SRP_SHA384: return SHA384( d, n, md ); + case SRP_SHA512: return SHA512( d, n, md );*/ default: return 0; }; @@ -280,49 +280,65 @@ 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_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; + /*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 ) +inline int mpz_num_bytes( const mpz_t op ) { - 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); + return (mpz_sizeinbase (op, 2) + 7) / 8; } -static BIGNUM * H_ns( SRP_HashAlgorithm alg, const BIGNUM * n, const unsigned char * bytes, int len_bytes ) +inline void mpz_to_bin( const mpz_t op, unsigned char * to ) +{ + mpz_export(to, NULL, 1, 1, 1, 0, op); +} + +inline void mpz_from_bin( const unsigned char * s, int len, mpz_t ret ) +{ + mpz_import(ret, len, 1, 1, 1, 0, s); +} + +static int H_nn( mpz_t result, SRP_HashAlgorithm alg, const mpz_t n1, const mpz_t n2 ) +{ + unsigned char buff[ SHA512_DIGEST_LENGTH ]; + int len_n1 = mpz_num_bytes(n1); + int len_n2 = mpz_num_bytes(n2); + int nbytes = len_n1 + len_n2; + unsigned char * bin = (unsigned char *) malloc( nbytes ); + if (!bin) { + return 0; + } + mpz_to_bin(n1, bin); + mpz_to_bin(n2, bin + len_n1); + hash( alg, bin, nbytes, buff ); + free(bin); + mpz_from_bin(buff, hash_length(alg), result); + return 1; +} + +static int H_ns( mpz_t result, SRP_HashAlgorithm alg, const unsigned char * n, int len_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, n, len_n ); memcpy( bin + len_n, bytes, len_bytes ); hash( alg, bin, nbytes, buff ); free(bin); - return BN_bin2bn(buff, hash_length(alg), NULL); + mpz_from_bin(buff, hash_length(alg), result); + return 1; } - -static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const char * username, const unsigned char * password, int password_len ) + +static int calculate_x( mpz_t result, SRP_HashAlgorithm alg, const unsigned char * salt, int salt_len, const char * username, const unsigned char * password, int password_len ) { unsigned char ucp_hash[SHA512_DIGEST_LENGTH]; HashCTX ctx; @@ -332,36 +348,36 @@ static BIGNUM * calculate_x( SRP_HashAlgorithm alg, const BIGNUM * salt, const c 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) ); + + return H_ns( result, alg, salt, salt_len, ucp_hash, hash_length(alg) ); } -static void update_hash_n( SRP_HashAlgorithm alg, HashCTX *ctx, const BIGNUM * n ) +static void update_hash_n( SRP_HashAlgorithm alg, HashCTX *ctx, const mpz_t n ) { - unsigned long len = BN_num_bytes(n); + unsigned long len = mpz_num_bytes(n); unsigned char * n_bytes = (unsigned char *) malloc( len ); if (!n_bytes) return; - BN_bn2bin(n, n_bytes); + mpz_to_bin(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 ) +static void hash_num( SRP_HashAlgorithm alg, const mpz_t n, unsigned char * dest ) { - int nbytes = BN_num_bytes(n); + int nbytes = mpz_num_bytes(n); unsigned char * bin = (unsigned char *) malloc( nbytes ); if(!bin) return; - BN_bn2bin(n, bin); + mpz_to_bin(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 ) +static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const unsigned char * s_bytes, int s_len, + const mpz_t A, const mpz_t B, const unsigned char * K ) { unsigned char H_N[ SHA512_DIGEST_LENGTH ]; unsigned char H_g[ SHA512_DIGEST_LENGTH ]; @@ -370,79 +386,88 @@ static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * 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 ); + hash_update( alg, &ctx, s_bytes, s_len ); 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 ) +static void calculate_H_AMK( SRP_HashAlgorithm alg, unsigned char *dest, const mpz_t 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 int fill_buff() +{ + g_rand_idx = 0; -static void init_random() -{ - if (g_initialized) - return; - #ifdef WIN32 HCRYPTPROV wctx; #else FILE *fp = 0; #endif - - unsigned char buff[64]; - #ifdef WIN32 CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - - CryptGenRandom(wctx, sizeof(buff), (BYTE*) buff); - + + CryptGenRandom(wctx, sizeof(g_rand_buff), (BYTE*) g_rand_buff); + CryptReleaseContext(wctx, 0); - g_initialized = 1; - + return 1; + #else fp = fopen("/dev/urandom", "r"); - + if (fp) { - fread(buff, sizeof(buff), 1, fp); + fread(g_rand_buff, sizeof(g_rand_buff), 1, fp); fclose(fp); - g_initialized = 1; + return 1; } #endif + return 0; +} +static void mpz_fill_random( mpz_t num ) +{ + // was call: BN_rand(num, 256, -1, 0); + if (RAND_BUFF_MAX - g_rand_idx < 32) + fill_buff(); + mpz_from_bin( (char *) (&g_rand_buff[g_rand_idx]), 32, num ); + g_rand_idx += 32; +} + +static void init_random() +{ if (g_initialized) - RAND_seed( buff, sizeof(buff) ); + return; + g_initialized = fill_buff(); } @@ -452,103 +477,89 @@ static void init_random() * ***********************************************************************************************************/ -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, +void srp_create_salted_verification_key( SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char * username_for_verifier, const unsigned char * password, int len_password, - const unsigned char ** bytes_s, int * len_s, + 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(); + mpz_t v; mpz_init(v); + mpz_t x; mpz_init(x); NGConstant * ng = new_ng( ng_type, n_hex, g_hex ); - if( !s || !v || !ctx || !ng ) + if( !v || !ng ) goto cleanup_and_exit; - init_random(); /* Only happens once */ - - BN_rand(s, 32, -1, 0); - - x = calculate_x( alg, s, username, password, len_password ); + if( bytes_s == NULL ) { + *len_s = 16; + if ( RAND_BUFF_MAX - g_rand_idx < 16 ) + fill_buff(); + *bytes_s = (const unsigned char *) malloc( sizeof(char) * 16 ); + memcpy( (unsigned char *) *bytes_s, &g_rand_buff + g_rand_idx, sizeof(char) * 16 ); + g_rand_idx += 16; + } - if( !x ) + + if( !calculate_x( x, alg, *bytes_s, *len_s, username_for_verifier, password, len_password )) 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 ); + mpz_powm(v, ng->g, x, ng->N); + + *len_v = mpz_num_bytes(v); + *bytes_v = (const unsigned char *) malloc( *len_v ); - if (!bytes_s || !bytes_v) + if (!bytes_v) goto cleanup_and_exit; - - BN_bn2bin(s, (unsigned char *) *bytes_s); - BN_bn2bin(v, (unsigned char *) *bytes_v); - + + mpz_to_bin(v, (unsigned char *) *bytes_v); + cleanup_and_exit: delete_ng( ng ); - BN_free(s); - BN_free(v); - BN_free(x); - BN_CTX_free(ctx); + mpz_clear(v); + mpz_clear(x); } /* 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_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 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_new(); - BIGNUM *S = BN_new(); - BIGNUM *b = 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; + mpz_t v; mpz_init(v); mpz_from_bin(bytes_v, len_v, v); + mpz_t A; mpz_init(A); mpz_from_bin(bytes_A, len_A, A); + mpz_t u; mpz_init(u); + mpz_t B; mpz_init(B); + mpz_t S; mpz_init(S); + mpz_t b; mpz_init(b); + mpz_t k; mpz_init(k); + mpz_t tmp1; mpz_init(tmp1); + mpz_t tmp2; mpz_init(tmp2); + int ulen = strlen(username) + 1; + NGConstant *ng = new_ng( ng_type, n_hex, g_hex ); + struct SRPVerifier *ver = 0; *len_B = 0; *bytes_B = 0; - - if( !s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ctx || !ng ) + + if( !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !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; @@ -559,39 +570,39 @@ struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_typ 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) ) + mpz_mod(tmp1, A, ng->N); + if ( mpz_sgn(tmp1) != 0 ) { - BN_rand(b, 256, -1, 0); - - k = H_nn(alg, ng->N, ng->g); - + mpz_fill_random(b); + + H_nn(k, alg, ng->N, ng->g); + /* B = kv + g^b */ - BN_mul(tmp1, k, v, ctx); - BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); - BN_add(B, tmp1, tmp2); - - u = H_nn(alg, A, B); - + mpz_mul(tmp1, k, v); + mpz_powm(tmp2, ng->g, b, ng->N); + mpz_add(B, tmp1, tmp2); + + H_nn(u, 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); - + mpz_powm(tmp1, v, u, ng->N); + mpz_mul(tmp2, A, tmp1); + mpz_powm(S, tmp2, b, ng->N); + hash_num(alg, S, ver->session_key); - - calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); + + calculate_M( alg, ng, ver->M, username, bytes_s, len_s, A, B, ver->session_key ); calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); - - *len_B = BN_num_bytes(B); + + *len_B = mpz_num_bytes(B); *bytes_B = malloc( *len_B ); - + if( !*bytes_B ) { free( (void*) ver->username ); @@ -600,29 +611,27 @@ struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_typ *len_B = 0; goto cleanup_and_exit; } - - BN_bn2bin( B, (unsigned char *) *bytes_B ); - + + mpz_to_bin( B, (unsigned char *) *bytes_B ); + 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); - + mpz_clear(v); + mpz_clear(A); + if (u) mpz_clear(u); + if (k) mpz_clear(k); + mpz_clear(B); + mpz_clear(S); + mpz_clear(b); + mpz_clear(tmp1); + mpz_clear(tmp2); + return ver; } - + void srp_verifier_delete( struct SRPVerifier * ver ) @@ -679,50 +688,54 @@ void srp_verifier_verify_session( struct SRPVerifier * ver, const unsigned char /*******************************************************************************/ -struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, +struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, + const char * username, const char * username_for_verifier, 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; + struct SRPUser *usr = (struct SRPUser *) malloc( sizeof(struct SRPUser) ); + int ulen = strlen(username) + 1; + int uvlen = strlen(username_for_verifier) + 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(); + + mpz_init(usr->a); + mpz_init(usr->A); + mpz_init(usr->S); 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; + + usr->username = (const char *) malloc(ulen); + usr->username_verifier = (const char *) malloc(uvlen); + 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); + + memcpy((char *)usr->username, username, ulen); + memcpy((char *)usr->username_verifier, username_for_verifier, uvlen); + 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); + mpz_clear(usr->a); + mpz_clear(usr->A); + mpz_clear(usr->S); if (usr->username) free((void*)usr->username); if (usr->password) @@ -732,7 +745,7 @@ struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const } free(usr); } - + return 0; } @@ -742,18 +755,18 @@ void srp_user_delete( struct SRPUser * usr ) { if( usr ) { - BN_free( usr->a ); - BN_free( usr->A ); - BN_free( usr->S ); - + mpz_clear( usr->a ); + mpz_clear( usr->A ); + mpz_clear( 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) + + if (usr->bytes_A) free( (char *)usr->bytes_A ); memset(usr, 0, sizeof(*usr)); @@ -792,18 +805,15 @@ 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, +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); + + mpz_fill_random(usr->a); + + mpz_powm(usr->A, usr->ng->g, usr->a, usr->ng->N); + + *len_A = mpz_num_bytes(usr->A); *bytes_A = malloc( *len_A ); if (!*bytes_A) @@ -813,70 +823,66 @@ void srp_user_start_authentication( struct SRPUser * usr, const char ** usernam *username = 0; return; } - - BN_bn2bin( usr->A, (unsigned char *) *bytes_A ); - + + mpz_to_bin( 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, +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(); + mpz_t B; mpz_init(B); mpz_from_bin(bytes_B, len_B, B); + mpz_t u; mpz_init(u); + mpz_t x; mpz_init(x); + mpz_t k; mpz_init(k); + mpz_t v; mpz_init(v); + mpz_t tmp1; mpz_init(tmp1); + mpz_t tmp2; mpz_init(tmp2); + mpz_t tmp3; mpz_init(tmp3); *len_M = 0; *bytes_M = 0; - if( !s || !B || !v || !tmp1 || !tmp2 || !tmp3 || !ctx ) + if( !B || !u || !x || !k || !v || !tmp1 || !tmp2 || !tmp3 ) goto cleanup_and_exit; - - u = H_nn(usr->hash_alg, usr->A, B); - if (!u) + if (!H_nn(u, usr->hash_alg, usr->A, B)) goto cleanup_and_exit; - - x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len ); + + calculate_x( x, usr->hash_alg, bytes_s, len_s, usr->username_verifier, usr->password, usr->password_len ); if (!x) goto cleanup_and_exit; - - k = H_nn(usr->hash_alg, usr->ng->N, usr->ng->g); + + H_nn(k, usr->hash_alg, usr->ng->N, usr->ng->g); if (!k) goto cleanup_and_exit; - + /* SRP-6a safety check */ - if ( !BN_is_zero(B) && !BN_is_zero(u) ) + if ( mpz_sgn(B) != 0 && mpz_sgn(u) != 0 ) { - BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx); - + mpz_powm(v, usr->ng->g, x, usr->ng->N); + /* 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); + mpz_mul(tmp1, u, x); + mpz_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ + mpz_powm(tmp1, usr->ng->g, x, usr->ng->N); + mpz_mul(tmp3, k, tmp1); /* tmp3 = k*(g^x) */ + mpz_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */ + mpz_powm(usr->S, tmp1, tmp2, usr->ng->N); 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_M( usr->hash_alg, usr->ng, usr->M, usr->username, bytes_s, len_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 ); @@ -884,24 +890,22 @@ void srp_user_process_challenge( struct SRPUser * usr, else { *bytes_M = NULL; - if (len_M) + 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); + + mpz_clear(B); + mpz_clear(u); + mpz_clear(x); + mpz_clear(k); + mpz_clear(v); + mpz_clear(tmp1); + mpz_clear(tmp2); + mpz_clear(tmp3); } - + void srp_user_verify_session( struct SRPUser * usr, const unsigned char * bytes_HAMK ) { diff --git a/srp.h b/srp.h index cf6b222..9fec76b 100644 --- a/srp.h +++ b/srp.h @@ -4,19 +4,19 @@ * https://github.com/cocagne/csrp * * The MIT License (MIT) - * - * Copyright (c) 2013 Tom Cocagne - * + * + * Copyright (c) 2013 Tom Cocagne, 2015 est31 + * * 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 @@ -24,33 +24,33 @@ * 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 + * 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) - * + * + * Dependencies: LibGMP + * * Usage: Refer to test_srp.c for a demonstration - * + * * Notes: - * This library allows multiple combinations of hashing algorithms and + * 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 + * 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 + * 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. */ @@ -70,62 +70,39 @@ typedef enum SRP_NG_CUSTOM } SRP_NGType; -typedef enum +typedef enum { - SRP_SHA1, - SRP_SHA224, + /*SRP_SHA1, + SRP_SHA224,*/ SRP_SHA256, - SRP_SHA384, - SRP_SHA512 + /*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. +/* Out: bytes_v, len_v + * + * The caller is responsible for freeing the memory allocated for bytes_v * - * 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. + * + * If bytes_s == NULL, it is filled with random data. The caller is responsible for freeing. */ -void srp_create_salted_verification_key( SRP_HashAlgorithm alg, - SRP_NGType ng_type, const char * username, +void srp_create_salted_verification_key( SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char * username_for_verifier, const unsigned char * password, int len_password, - const unsigned char ** bytes_s, int * len_s, + 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. - * + * * On failure, bytes_B will be set to NULL and len_B will be set to 0 - * + * * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */ 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_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, @@ -149,16 +126,17 @@ int srp_verifier_get_session_key_length( struct SRPVerifier * /* 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 * 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, +struct SRPUser * srp_user_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, + const char * username, const char * username_for_verifier, 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); @@ -172,16 +150,16 @@ const unsigned char * srp_user_get_session_key( struct SRPUser * usr, int * key_ 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, +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 +/* 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, +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 ); diff --git a/test_srp.c b/test_srp.c index d3554be..cf64642 100644 --- a/test_srp.c +++ b/test_srp.c @@ -8,7 +8,7 @@ #define NITER 100 -#define TEST_HASH SRP_SHA1 +#define TEST_HASH SRP_SHA256 #define TEST_NG SRP_NG_1024 unsigned long long get_usec() @@ -76,7 +76,7 @@ int main( int argc, char * argv[] ) for( i = 0; i < NITER; i++ ) { - usr = srp_user_new( alg, ng_type, username, + usr = srp_user_new( alg, ng_type, username, username, (const unsigned char *)password, strlen(password), n_hex, g_hex );