/* * 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 #include #include /* abs (), srand (), rand () */ #include /* memcpy (), memmove (), memset (), strcmp (), strlen () */ #include #include #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); }