Port to LibGMP

master
est31 2015-04-12 03:40:29 +02:00
parent c0069bcc4e
commit 8c815b2214
9 changed files with 1424 additions and 485 deletions

View File

@ -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

138
README.md
View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "srp.h"
int main( int argc, char * argv[] )
{
int auth_failed = 1;
struct SRPVerifier * ver;
struct SRPUser * usr;
const unsigned char * bytes_s = 0;
const unsigned char * bytes_v = 0;
const unsigned char * bytes_A = 0;
const unsigned char * bytes_B = 0;
const unsigned char * bytes_M = 0;
const unsigned char * bytes_HAMK = 0;
int len_s = 0;
int len_v = 0;
int len_A = 0;
int len_B = 0;
int len_M = 0;
const char * username = "testuser";
const char * password = "password";
const char * auth_username = 0;
SRP_HashAlgorithm alg = SRP_SHA1;
SRP_NGType ng_type = SRP_NG_2048;
/* Create a salt+verification key for the user's password. The salt and
* key need to be computed at the time the user's password is set and
* must be stored by the server-side application for use during the
* authentication process.
*/
srp_create_salted_verification_key( alg, ng_type, username,
(const unsigned char *)password,
strlen(password),
&bytes_s, &len_s,
&bytes_v, &len_v,
NULL, NULL );
/* Begin authentication process */
usr = srp_user_new( alg, ng_type, username,
(const unsigned char *)password,
strlen(password), NULL, NULL );
srp_user_start_authentication( usr, &auth_username, &bytes_A, &len_A );
/* User -> Host: (username, bytes_A) */
ver = srp_verifier_new( alg, ng_type, username, bytes_s, len_s, bytes_v, len_v,
bytes_A, len_A, & bytes_B, &len_B, NULL, NULL );
if ( !bytes_B ) {
printf("Verifier SRP-6a safety check violated!\n");
goto auth_failed;
}
/* Host -> User: (bytes_s, bytes_B) */
srp_user_process_challenge( usr, bytes_s, len_s, bytes_B, len_B, &bytes_M, &len_M );
if ( !bytes_M ) {
printf("User SRP-6a safety check violation!\n");
goto auth_failed;
}
/* User -> Host: (bytes_M) */
srp_verifier_verify_session( ver, bytes_M, &bytes_HAMK );
if ( !bytes_HAMK ) {
printf("User authentication failed!\n");
goto auth_failed;
}
/* Host -> User: (HAMK) */
srp_user_verify_session( usr, bytes_HAMK );
if ( !srp_user_is_authenticated(usr) ) {
printf("Server authentication failed!\n");
goto auth_failed;
}
auth_failed = 0; /* auth success! */
auth_failed:
srp_verifier_delete( ver );
srp_user_delete( usr );
free( (char *)bytes_s );
free( (char *)bytes_v );
return auth_failed;
}
```
For an usage example, see test_srp.c

428
sha/md32_common.h Normal file
View File

@ -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
*
* <appro@fy.chalmers.se>
*/
#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. <appro@fy.chalmers.se> */
# 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...
* <appro@fy.chalmers.se>
*/
# 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
* <weidai@eskimo.com> 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.
* <appro@fy.chalmers.se>
*/
# 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.
* <appro@fy.chalmers.se>
*/
# define MD32_REG_T int
# endif
#endif

154
sha/sha2.h Normal file
View File

@ -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 <stddef.h>
#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

404
sha/sha256.c Normal file
View File

@ -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 <stdlib.h>
# include <string.h>
# 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;nn<SHA224_DIGEST_LENGTH/4;nn++) \
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
break; \
case SHA256_DIGEST_LENGTH: \
for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++) \
{ ll=(c)->h[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 */

60
sha/test_sha256.c Normal file
View File

@ -0,0 +1,60 @@
#include <stdio.h>
#include <stdlib.h>
#include <sha2.h>
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;
}

606
srp.c
View File

@ -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 <MTest31@outlook.com>
*
* 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 <string.h>
#include <stdio.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <gmp.h>
#include <sha/sha2.h>
#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 )
{

110
srp.h
View File

@ -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 <MTest31@outlook.com>
*
* 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 );

View File

@ -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 );