1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-01-31 03:12:44 +00:00
transmission/libtransmission/crypto-utils.c
Mike Gelfand 1d58af5082 Increase BASE64 encoding size when using system libb64
Remove BASE64 reference testing as it's only libb64 now.
Improve the test to ignore \r and \n when comparing BASE-encoded
strings to not fail on system (unpatched) libb64.
2015-01-03 21:35:20 +00:00

290 lines
6.3 KiB
C

/*
* This file Copyright (C) 2007-2014 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h> /* abs (), srand (), rand () */
#include <string.h> /* memcpy (), memmove (), memset (), strcmp (), strlen () */
#include <b64/cdecode.h>
#include <b64/cencode.h>
#include "transmission.h"
#include "crypto-utils.h"
#include "utils.h"
/***
****
***/
void
tr_dh_align_key (uint8_t * key_buffer,
size_t key_size,
size_t buffer_size)
{
assert (key_size <= buffer_size);
/* DH can generate key sizes that are smaller than the size of
key buffer with exponentially decreasing probability, in which case
the msb's of key buffer need to be zeroed appropriately. */
if (key_size < buffer_size)
{
const size_t offset = buffer_size - key_size;
memmove (key_buffer + offset, key_buffer, key_size);
memset (key_buffer, 0, offset);
}
}
/***
****
***/
bool
tr_sha1 (uint8_t * hash,
const void * data1,
int data1_length,
...)
{
tr_sha1_ctx_t sha;
if ((sha = tr_sha1_init ()) == NULL)
return false;
if (tr_sha1_update (sha, data1, data1_length))
{
va_list vl;
const void * data;
va_start (vl, data1_length);
while ((data = va_arg (vl, const void *)) != NULL)
{
const int data_length = va_arg (vl, int);
assert (data_length >= 0);
if (!tr_sha1_update (sha, data, data_length))
break;
}
va_end (vl);
/* did we reach the end of argument list? */
if (data == NULL)
return tr_sha1_final (sha, hash);
}
tr_sha1_final (sha, NULL);
return false;
}
/***
****
***/
int
tr_rand_int (int upper_bound)
{
int noise;
assert (upper_bound > 0);
while (tr_rand_buffer (&noise, sizeof (noise)))
{
noise = abs(noise) % upper_bound;
/* abs(INT_MIN) is undefined and could return negative value */
if (noise >= 0)
return noise;
}
/* fall back to a weaker implementation... */
return tr_rand_int_weak (upper_bound);
}
int
tr_rand_int_weak (int upper_bound)
{
static bool init = false;
assert (upper_bound > 0);
if (!init)
{
srand (tr_time_msec ());
init = true;
}
return rand () % upper_bound;
}
/***
****
***/
char *
tr_ssha1 (const char * plain_text)
{
enum { saltval_len = 8,
salter_len = 64 };
static const char * salter = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"./";
size_t i;
unsigned char salt[saltval_len];
uint8_t sha[SHA_DIGEST_LENGTH];
char buf[2 * SHA_DIGEST_LENGTH + saltval_len + 2];
tr_rand_buffer (salt, saltval_len);
for (i = 0; i < saltval_len; ++i)
salt[i] = salter[salt[i] % salter_len];
tr_sha1 (sha, plain_text, strlen (plain_text), salt, (size_t) saltval_len, NULL);
tr_sha1_to_hex (&buf[1], sha);
memcpy (&buf[1 + 2 * SHA_DIGEST_LENGTH], &salt, saltval_len);
buf[1 + 2 * SHA_DIGEST_LENGTH + saltval_len] = '\0';
buf[0] = '{'; /* signal that this is a hash. this makes saving/restoring easier */
return tr_strdup (buf);
}
bool
tr_ssha1_matches (const char * ssha1,
const char * plain_text)
{
char * salt;
size_t saltlen;
char * my_ssha1;
uint8_t buf[SHA_DIGEST_LENGTH];
bool result;
const size_t sourcelen = strlen (ssha1);
/* extract the salt */
if (sourcelen < 2 * SHA_DIGEST_LENGTH - 1)
return false;
saltlen = sourcelen - 2 * SHA_DIGEST_LENGTH - 1;
salt = tr_malloc (saltlen);
memcpy (salt, ssha1 + 2 * SHA_DIGEST_LENGTH + 1, saltlen);
/* hash pass + salt */
my_ssha1 = tr_malloc (2 * SHA_DIGEST_LENGTH + saltlen + 2);
tr_sha1 (buf, plain_text, strlen (plain_text), salt, saltlen, NULL);
tr_sha1_to_hex (&my_ssha1[1], buf);
memcpy (my_ssha1 + 1 + 2 * SHA_DIGEST_LENGTH, salt, saltlen);
my_ssha1[1 + 2 * SHA_DIGEST_LENGTH + saltlen] = '\0';
my_ssha1[0] = '{';
result = strcmp (ssha1, my_ssha1) == 0;
tr_free (my_ssha1);
tr_free (salt);
return result;
}
/***
****
***/
void *
tr_base64_encode (const void * input,
size_t input_length,
size_t * output_length)
{
char * ret;
if (input != NULL)
{
if (input_length != 0)
{
size_t ret_length = 4 * ((input_length + 2) / 3);
base64_encodestate state;
#ifdef USE_SYSTEM_B64
/* Additional space is needed for newlines if we're using unpatched libb64 */
ret_length += ret_length / 72 + 1;
#endif
ret = tr_new (char, ret_length + 1);
base64_init_encodestate (&state);
ret_length = base64_encode_block (input, input_length, ret, &state);
ret_length += base64_encode_blockend (ret + ret_length, &state);
if (output_length != NULL)
*output_length = ret_length;
ret[ret_length] = '\0';
return ret;
}
else
ret = tr_strdup ("");
}
else
{
ret = NULL;
}
if (output_length != NULL)
*output_length = 0;
return ret;
}
void *
tr_base64_encode_str (const char * input,
size_t * output_length)
{
return tr_base64_encode (input, input == NULL ? 0 : strlen (input), output_length);
}
void *
tr_base64_decode (const void * input,
size_t input_length,
size_t * output_length)
{
char * ret;
if (input != NULL)
{
if (input_length != 0)
{
size_t ret_length = input_length / 4 * 3;
base64_decodestate state;
ret = tr_new (char, ret_length + 1);
base64_init_decodestate (&state);
ret_length = base64_decode_block (input, input_length, ret, &state);
if (output_length != NULL)
*output_length = ret_length;
ret[ret_length] = '\0';
return ret;
}
else
ret = tr_strdup ("");
}
else
{
ret = NULL;
}
if (output_length != NULL)
*output_length = 0;
return ret;
}
void *
tr_base64_decode_str (const char * input,
size_t * output_length)
{
return tr_base64_decode (input, input == NULL ? 0 : strlen (input), output_length);
}