minetest-world-manager/src/minetest_world_manager_srp.c

213 lines
8.8 KiB
C

/*
* Secure Remote Password 6a implementation
* https://github.com/est31/csrp-gmp
*
* The MIT License (MIT)
*
* Copyright (c) 2016 YuGiOhJCJ <yugiohjcj@1s.fr>
* Copyright (c) 2015 est31 <MTest31@outlook.com>
* Copyright (c) 2010, 2013 Tom Cocagne
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include <gmp.h> /* for mpz_init */
#include <openssl/sha.h> /* for SHA1_Init */
#include <stdio.h> /* for fopen */
#include <stdlib.h> /* for free */
#include <string.h> /* for memcpy */
#include "minetest_world_manager_print.h" /* for minetest_world_manager_print_error */
#include "minetest_world_manager_srp.h" /* for minetest_world_manager_srp_create_salted_verification_key */
#include "minetest_world_manager_string.h" /* for minetest_world_manager_string_get_field */
#define minetest_world_manager_srp_rand_buff_max 128
typedef struct
{
mpz_t n;
mpz_t g;
} minetest_world_manager_srp_ng_constant_t;
static int minetest_world_manager_srp_calculate_x(mpz_t result, const unsigned char *salt, size_t salt_len, const char *username, const unsigned char *password, size_t password_len);
static int minetest_world_manager_srp_create_salted_verification_return(int value, minetest_world_manager_srp_ng_constant_t *ng, mpz_t v, mpz_t x);
static void minetest_world_manager_srp_delete_ng(minetest_world_manager_srp_ng_constant_t *ng);
static char *minetest_world_manager_srp_get_prime();
static int minetest_world_manager_srp_h_ns(mpz_t result, const unsigned char *n, size_t len_n, const unsigned char *bytes, size_t len_bytes);
static void minetest_world_manager_srp_mpz_from_bin(const unsigned char *s, size_t len, mpz_t ret);
static int minetest_world_manager_srp_mpz_num_bytes(const mpz_t op);
static void minetest_world_manager_srp_mpz_to_bin(const mpz_t op, unsigned char *to);
static minetest_world_manager_srp_ng_constant_t *minetest_world_manager_srp_new_ng();
static int minetest_world_manager_srp_calculate_x(mpz_t result, const unsigned char *salt, size_t salt_len, const char *username, const unsigned char *password, size_t password_len)
{
unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, username, strlen(username));
SHA256_Update(&ctx, ":", 1);
SHA256_Update(&ctx, password, password_len);
SHA256_Final(ucp_hash, &ctx);
return minetest_world_manager_srp_h_ns(result, salt, salt_len, ucp_hash, SHA256_DIGEST_LENGTH);
}
static int minetest_world_manager_srp_create_salted_verification_return(int value, minetest_world_manager_srp_ng_constant_t *ng, mpz_t v, mpz_t x)
{
if(ng != NULL)
{
minetest_world_manager_srp_delete_ng(ng);
ng = NULL;
}
if(v != NULL)
{
mpz_clear(v);
v = NULL;
}
if(x != NULL)
{
mpz_clear(x);
x = NULL;
}
return value;
}
static void minetest_world_manager_srp_delete_ng(minetest_world_manager_srp_ng_constant_t *ng)
{
if(ng != NULL)
{
if(ng->n != NULL)
mpz_clear(ng->n);
if(ng->g != NULL)
mpz_clear(ng->g);
free(ng);
ng = NULL;
}
}
static char *minetest_world_manager_srp_get_prime()
{
char *result = NULL;
const char *n01 = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294";
const char *n02 = "3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D";
const char *n03 = "CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB";
const char *n04 = "D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74";
const char *n05 = "7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A";
const char *n06 = "436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D";
const char *n07 = "5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73";
const char *n08 = "03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6";
const char *n09 = "94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F";
const char *n10 = "9E4AFF73";
result = malloc(sizeof(char) * (strlen(n01) + strlen(n02) + strlen(n03) + strlen(n04) + strlen(n05) + strlen(n06) + strlen(n07) + strlen(n08) + strlen(n09) + strlen(n10) + 1));
if(result == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to allocate memory for the \"%s%s%s%s%s%s%s%s%s%s\" string containing the hexadecimal value for the prime.", n01, n02, n03, n04, n05, n06, n07, n08, n09, n10);
return NULL;
}
if(sprintf(result, "%s%s%s%s%s%s%s%s%s%s", n01, n02, n03, n04, n05, n06, n07, n08, n09, n10) < 0)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to create the \"%s%s%s%s%s%s%s%s%s%s\" string containing the hexadecimal value for the prime.", n01, n02, n03, n04, n05, n06, n07, n08, n09, n10);
free(result);
return NULL;
}
return result;
}
static int minetest_world_manager_srp_h_ns(mpz_t result, const unsigned char *n, size_t len_n, const unsigned char *bytes, size_t len_bytes)
{
unsigned char buff[SHA512_DIGEST_LENGTH];
size_t nbytes = len_n + len_bytes;
unsigned char *bin = (unsigned char *) malloc(nbytes);
if(bin == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to allocate memory to calculate x.");
return -1;
}
memcpy(bin, n, len_n);
memcpy(bin + len_n, bytes, len_bytes);
SHA256(bin, nbytes, buff);
free(bin);
minetest_world_manager_srp_mpz_from_bin(buff, SHA256_DIGEST_LENGTH, result);
return 0;
}
static void minetest_world_manager_srp_mpz_from_bin(const unsigned char *s, size_t len, mpz_t ret)
{
mpz_import(ret, len, 1, 1, 1, 0, s);
}
static int minetest_world_manager_srp_mpz_num_bytes(const mpz_t op)
{
return (mpz_sizeinbase(op, 2) + 7) / 8;
}
static void minetest_world_manager_srp_mpz_to_bin(const mpz_t op, unsigned char *to)
{
mpz_export(to, NULL, 1, 1, 1, 0, op);
}
static minetest_world_manager_srp_ng_constant_t *minetest_world_manager_srp_new_ng()
{
int rv = 0;
char *n_hex = NULL;
char *g_hex = NULL;
minetest_world_manager_srp_ng_constant_t *ng = malloc(sizeof(minetest_world_manager_srp_ng_constant_t));
if(ng == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to create the structure for the prime and the generator.");
return NULL;
}
mpz_init(ng->n);
mpz_init(ng->g);
n_hex = minetest_world_manager_srp_get_prime();
if(n_hex == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to get the hexadecimal value for the prime.");
minetest_world_manager_srp_delete_ng(ng);
return NULL;
}
g_hex = "2";
rv = mpz_set_str(ng->n, n_hex, 16);
rv = rv | mpz_set_str(ng->g, g_hex, 16);
if(rv)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to convert the \"%s\" string to an hexadecimal.");
minetest_world_manager_srp_delete_ng(ng);
free(n_hex);
return NULL;
}
free(n_hex);
return ng;
}
int minetest_world_manager_srp_create_salted_verification_key(const char *username_for_verifier, const unsigned char *password, size_t len_password, unsigned char **bytes_s, size_t *len_s, unsigned char **bytes_v, size_t *len_v)
{
minetest_world_manager_srp_ng_constant_t *ng = NULL;
mpz_t v;
mpz_t x;
mpz_init(v);
mpz_init(x);
ng = minetest_world_manager_srp_new_ng();
if(ng == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to create the group parameters.");
return minetest_world_manager_srp_create_salted_verification_return(-1, ng, v, x);
}
if(minetest_world_manager_srp_calculate_x(x, *bytes_s, *len_s, username_for_verifier, password, len_password) == -1)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to calculate x.");
return minetest_world_manager_srp_create_salted_verification_return(-1, ng, v, x);
}
mpz_powm(v, ng->g, x, ng->n);
*len_v = minetest_world_manager_srp_mpz_num_bytes(v);
*bytes_v = (unsigned char *) malloc(*len_v);
if(*bytes_v == NULL)
{
minetest_world_manager_print_error(__FILE__, __LINE__, "Unable to allocate memory for the key.");
return minetest_world_manager_srp_create_salted_verification_return(-1, ng, v, x);
}
minetest_world_manager_srp_mpz_to_bin(v, *bytes_v);
return minetest_world_manager_srp_create_salted_verification_return(0, ng, v, x);
}