Rev. | 51bfccf92874bd05dff97e3b85edceca279a6673 |
---|---|
Size | 43,269 bytes |
Time | 2023-12-27 23:02:38 |
Author | simphone |
Log Message | simphone 0.9.2 |
/**
key management, including audio entropy collection and conversion of seed between binary and ASCII
Copyright (c) 2020-2023 The Creators of Simphone
See the file COPYING.LESSER.txt for copying permission.
**/
#include "config.h"
#include "spth.h"
#include "const.h"
#include "logger.h"
#include "table.h"
#include "error.h"
#include "utils.h"
#include "system.h"
#include "crypto.h"
#include "sssl.h"
#include "file.h"
#include "keygen.h"
#include "contact.h"
#include "param.h"
#include "proxy.h"
#include "audio.h"
#include "api.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#define SIM_MODULE "keygen"
#define CONTACT_ADDRESS_SYSTEM "SMPHNEHLPEY5A7GFTMLVPNK29V2C8YH9SFS6Y"
#define KEY_SEED_CHECK_BITS 31 /* number of seed checksum bytes */
#define KEY_SEED_WORD_BITS 6 /* number of bits per country name */
#define KEY_MAX_WORD_LENGTH 17 /* maximal country name length */
#define KEY_MAX_SEED_WORDS ((SIM_MAX_SEED_BITS + KEY_SEED_CHECK_BITS + KEY_SEED_WORD_BITS - 1) / KEY_SEED_WORD_BITS)
#define KEY_EC "ec"
#define KEY_RSA "rsa"
#define KEY_SEED "seed"
#define KEY_NO_SEED "SEED"
#define KEY_REVOKED "revoked"
struct key_time {
simnumber cputime;
simnumber realtime;
};
static simtype key_file; /* keyed by KEY_EC / KEY_RSA, value is private key */
static simtype key_table; /* keyed by KEY_EC / KEY_RSA, value is public key */
static const simbyte *key_pointers[] = { key_2048, key_4096, key_8192, key_16384 };
static const unsigned *key_lengths[] = { &key_2048_size, &key_4096_size, &key_8192_size, &key_16384_size };
static simtype key_country_table; /* keyed by country string, value is index into key_countries (seed word value) */
static const char ***key_countries;
static int key_countries_size;
static simtype random_buffers[4];
static unsigned random_bytes[4], random_bits[4];
struct keygen_arg {
unsigned *state; /* pointer to api_init_state */
unsigned type; /* SIM_KEY_BP512R1_xxx (as given to sim_key_generate_, but not SIM_KEY_NONE */
simtype key; /* copy of *seed as given to sim_key_generate_ or nil if nil was given */
simtype seed; /* *seed given to sim_key_generate_ converted to binary or nil if called with SIM_KEY_NONE */
};
static void key_load_countries (void) {
const char **p = key_country_strings;
int n = 0, m = 0;
for (; *p; n++)
for (; *p++; m++) {}
key_countries = sim_new (key_countries_size = n * sizeof (**key_countries));
key_country_table = table_new (m * 3);
n = 0;
for (p = key_country_strings - 1; *++p;)
for (key_countries[n++] = p; *p; table_add_number (key_country_table, *p++, n)) {}
if (n != 1 << KEY_SEED_WORD_BITS)
LOG_FATAL_ (SIM_OK, "countries %d != %d\n", n, 1 << KEY_SEED_WORD_BITS);
}
static void key_unload_countries (void) {
table_free (key_country_table);
sim_free ((void *) key_countries, key_countries_size);
}
#define KEY_LOOKUP_DISTANCE(i, j) dd[(i) * (length2 + 2) + (j)]
#define KEY_MIN2(a, b) ((a) < (b) ? (a) : (b))
#define KEY_MIN3(a, b, c) ((a) < (b) ? KEY_MIN2 ((a), (c)) : KEY_MIN2 ((b), (c)))
#define KEY_MIN4(a, b, c, d) ((a) < (b) ? KEY_MIN3 ((a), (c), (d)) : KEY_MIN3 ((b), (c), (d)))
static int sim_convert_strings_to_distance (const simbyte *string1, int length1,
const simbyte *string2, int length2) {
int i, j, dist, i1, j1, db, infinity = length1 + length2, len;
int *dd = sim_new (len = (length1 + 2) * (length2 + 2) * sizeof (*dd)), da[256];
KEY_LOOKUP_DISTANCE (0, 0) = infinity;
for (i = 0; i < length1 + 1; i++) {
KEY_LOOKUP_DISTANCE (i + 1, 1) = i;
KEY_LOOKUP_DISTANCE (i + 1, 0) = infinity;
}
for (j = 0; j < length2 + 1; j++) {
KEY_LOOKUP_DISTANCE (1, j + 1) = j;
KEY_LOOKUP_DISTANCE (0, j + 1) = infinity;
}
memset (da, 0, sizeof (da));
for (i = 1; i < length1 + 1; i++) {
db = 0;
for (j = 1; j < length2 + 1; j++) {
i1 = da[string2[j - 1]];
j1 = db;
if ((dist = string1[i - 1] != string2[j - 1]) == 0)
db = j;
KEY_LOOKUP_DISTANCE (i + 1, j + 1) = KEY_MIN4 (KEY_LOOKUP_DISTANCE (i, j) + dist,
KEY_LOOKUP_DISTANCE (i + 1, j) + 1,
KEY_LOOKUP_DISTANCE (i, j + 1) + 1,
KEY_LOOKUP_DISTANCE (i1, j1) + (i - i1 - 1) + 1 + (j - j1 - 1));
}
da[string1[i - 1]] = i;
}
dist = KEY_LOOKUP_DISTANCE (length1 + 1, length2 + 1);
sim_free (dd, len);
return dist;
}
#define KEY_CALC_LAST_DIGIT(digits) (((digits) / 8 * 2 + (digits) % 8) % 9 + 1)
simtype convert_seed_to_string (const simtype seed, unsigned type, simbool numeric) {
simtype hash, buffer = string_buffer_new ((seed.len * 8 + KEY_SEED_CHECK_BITS + KEY_SEED_WORD_BITS - 1 + 7) / 8);
simbyte *p = buffer.str, t = (simbyte) type;
unsigned l = 0;
int a, b = 0, count = (seed.len * 8 + KEY_SEED_CHECK_BITS + KEY_SEED_WORD_BITS - 1) / KEY_SEED_WORD_BITS;
simtype buf = t == type ? string_buffer_new ((KEY_MAX_WORD_LENGTH + 1) * count) : nil ();
void *md = sim_crypt_md_new (CRYPT_MD_SHA);
memcpy (buffer.str, seed.str, seed.len);
hash = sim_crypt_md_hash (md, nil (), pointer_new_len (seed.str, seed.len), pointer_new_len (&t, sizeof (t)));
memcpy (buffer.str + seed.len, hash.str, buffer.len - seed.len);
key_load_countries ();
for (a = *p++; count; count--) {
int n = 0, c = KEY_SEED_WORD_BITS;
while (c--) {
n <<= 1;
if (a & 128)
++n;
a <<= 1;
if (! (++b & 7))
a = *p++;
}
if (! numeric) {
strcpy ((char *) buf.str + l, *key_countries[n]);
} else
sprintf ((char *) buf.str + l, "%02o%d", n, KEY_CALC_LAST_DIGIT (n));
l = strlen (buf.ptr);
buf.str[l++] = '-';
}
key_unload_countries ();
string_buffer_free (buffer);
string_free (hash);
return string_buffer_truncate (buf, l ? l - 1 : 0);
}
static int sim_convert_seed_type (const int *values, int count, simbyte type, simbyte *seed) {
simbyte *p = seed;
int a = 0, b = 0, i, l = (count * KEY_SEED_WORD_BITS - KEY_SEED_CHECK_BITS) / 8;
simtype hash;
if (l > 0) {
void *md = sim_crypt_md_new (CRYPT_MD_SHA);
for (i = 0; i < count; i++) {
int v = values[i], c = KEY_SEED_WORD_BITS;
while (c--) {
a <<= 1;
if (v & 1 << (KEY_SEED_WORD_BITS - 1))
a++;
v <<= 1;
if (! (++b & 7)) {
*p++ = (simbyte) a;
a = 0;
}
}
}
if (b & 7)
*p++ = (simbyte) (a << (8 - (b & 7)));
hash = sim_crypt_md_hash (md, nil (), pointer_new_len (seed, l), pointer_new_len (&type, sizeof (type)));
i = b - l * 8 - KEY_SEED_CHECK_BITS / 8 * 8 - 1; /* number of rest high bits (7 to 13) to compare - 1 */
a = (hash.str[KEY_SEED_CHECK_BITS / 8] << 8 | hash.str[KEY_SEED_CHECK_BITS / 8 + 1]) & -(1 << (15 - i));
b = (seed[l + KEY_SEED_CHECK_BITS / 8] << 8 | seed[l + KEY_SEED_CHECK_BITS / 8 + 1]) & -(1 << (15 - i));
if (a != b || memcmp (seed + l, hash.ptr, KEY_SEED_CHECK_BITS / 8))
l = 0;
string_free (hash);
}
return l > 0 ? l : 0;
}
static unsigned sim_convert_seed (const int *values, int count, simbool exact, unsigned *type, simbyte *seed) {
unsigned len, mintype = exact ? 0 : SIM_KEY_BP512R1_RSA2K, maxtype = 255;
if (! exact)
maxtype = count < 32 ? SIM_KEY_BP512R1_RSA4K : count < 41 ? SIM_KEY_BP512R1_RSA8K : SIM_KEY_BP512R1_RSA16K;
for (*type = mintype; *type <= maxtype; ++*type)
if ((len = sim_convert_seed_type (values, count, (simbyte) *type, seed)) != 0)
return len;
*type = SIM_KEY_NONE;
return 0;
}
simtype convert_string_to_seed (const char *string, unsigned *type) {
int i, j, count = 0, distance = 0, maxdist = 0, retry = 0, maxretry = ((unsigned) 1 << KEY_SEED_CHECK_BITS) / 1000;
simtype seed, ascii;
const char *s = string;
char *t, *words[KEY_MAX_SEED_WORDS];
int values[KEY_MAX_SEED_WORDS + 2], *distances[KEY_MAX_SEED_WORDS], tmp[KEY_MAX_SEED_WORDS];
unsigned seedlen;
*type = SIM_KEY_NONE;
if (! s)
return nil ();
/* convert UTF-8 string to lowercase ASCII */
for (t = (ascii = string_buffer_new (strlen (s) + 1)).ptr; *s; s++)
if ((simbyte) *s < 128) {
*t++ = (char) tolower (*s);
} else if ((simbyte) *s == 195 && (simbyte) s[1] >= 128 && (simbyte) s[1] <= 158) {
*t++ = *s;
*t++ = *++s + 32;
}
*t = 0;
/* split ASCII string to words */
for (t = ascii.ptr; *t;) {
if (count == KEY_MAX_SEED_WORDS) {
string_buffer_free (ascii);
return nil ();
}
for (; *t == '-' || isspace (*t); t++) {}
words[count++] = t;
for (; *t && *t != '-' && ! isspace (*t); t++) {}
if (*t) {
*t++ = 0;
} else if (! *words[count - 1])
--count;
}
/* convert words to values or to distances to each possible value, if exact value not found */
key_load_countries ();
memset (distances, 0, sizeof (distances));
for (i = 0; i < count; i++) {
values[i + 1] = (int) table_get_number (key_country_table, words[i]) - 1;
if (isdigit (*words[i])) {
for (j = atoi (words[i]); j >= 1000; j /= 10) {}
values[i + 1] = j / 100 * 8 + j / 10 % 10;
if (strlen (words[i]) < 3 && j < 80 && j % 10 < 8) {
values[i + 1] = j / 10 * 8 + j % 10;
} else if (j >= 800 || j / 10 % 10 >= 8 || KEY_CALC_LAST_DIGIT (values[i + 1]) != j % 10) {
int *dist = distances[i] = sim_new ((1 << KEY_SEED_WORD_BITS) * sizeof (*dist));
for (j = 0; j < 1 << KEY_SEED_WORD_BITS; j++) {
char word[5];
sprintf (word, "%02o%d", j, KEY_CALC_LAST_DIGIT (j));
dist[j] = sim_convert_strings_to_distance ((simbyte *) words[i], strlen (words[i]),
(simbyte *) word, strlen (word));
if (dist[j] > maxdist)
maxdist = dist[j];
}
values[i + 1] = -1;
}
} else if (values[i + 1] < 0) {
int *dist = distances[i] = sim_new ((1 << KEY_SEED_WORD_BITS) * sizeof (*dist));
const char **names = key_country_strings;
for (; *names; names++) {
int mindist = 1 << 30;
for (; *names; names++) {
int d = sim_convert_strings_to_distance ((simbyte *) words[i], strlen (words[i]),
(const simbyte *) *names, strlen (*names));
if (d < mindist)
mindist = d;
}
if ((*dist++ = mindist) > maxdist)
maxdist = mindist;
}
}
}
/* start trying to match with all possible values in order of likelihood up to the retry limit */
maxretry = count < 32 ? maxretry / 2 : count < 41 ? maxretry * 3 / 8 : maxretry / 4;
seed = string_buffer_new (2 + (count * KEY_SEED_WORD_BITS + 7) / 8);
seedlen = 0;
restart:
while (retry < maxretry && ++distance <= maxdist) {
for (i = 0; i < count; i++)
if (distances[i]) {
tmp[i] = -1;
for (j = 0; j < 1 << KEY_SEED_WORD_BITS; j++)
if (distances[i][j] <= distance) {
tmp[i] = j;
break;
}
if (tmp[i] < 0)
goto restart;
}
do {
for (i = 0; i < count; i++)
if (distances[i])
values[i + 1] = tmp[i];
if ((seedlen = sim_convert_seed (values + 1, count, false, type, seed.str)) != 0)
goto done; /* got a match */
if ((retry += count) >= maxretry)
break;
for (i = 0; i < count; i++)
if (distances[i]) {
j = tmp[i];
tmp[i] = -1;
while (++j < 1 << KEY_SEED_WORD_BITS)
if (distances[i][j] <= distance) {
tmp[i] = j;
break;
}
if (tmp[i] >= 0)
break;
for (j = 0; j < 1 << KEY_SEED_WORD_BITS; j++)
if (distances[i][j] <= distance) {
tmp[i] = j;
break;
}
}
} while (i < count);
}
if (maxdist || ! count) { /* report words that were not matched */
STRING_BUFFER_FREE (&seed, string_buffer_new (strlen (string) + 2));
seed.str[0] = 0;
for (i = 0; i < count; i++)
if (distances[i])
strcat (strcat (seed.ptr, words[i]), " ");
seedlen = strlen (seed.ptr);
} else if ((seedlen = sim_convert_seed (values + 1, count, true, type, seed.str)) == 0) {
for (i = 1; i <= count; i++) { /* try to correct a replaced word */
int n = values[i];
for (j = 0; j < 1 << KEY_SEED_WORD_BITS; j++) {
values[i] = j;
if ((seedlen = sim_convert_seed (values + 1, count, false, type, seed.str)) != 0)
goto done;
}
values[i] = n;
}
for (i = 0; i <= count; i++) /* try to insert a deleted word */
for (j = 0; j < 1 << KEY_SEED_WORD_BITS; j++) {
values[i] = j;
if ((seedlen = sim_convert_seed (values, count + 1, false, type, seed.str)) != 0)
goto done;
values[i] = values[i + 1];
}
for (i = 0; i < count; i++) { /* try to delete an inserted word */
for (j = 0; j < count - 1; j++)
tmp[j] = values[j < i ? j : j + 1];
if ((seedlen = sim_convert_seed (tmp, count - 1, false, type, seed.str)) != 0)
goto done;
}
for (i = 0; i < count - 1; i++) { /* try to correct a swapped word */
j = values[i];
values[i] = values[i + 1];
values[i + 1] = j;
if ((seedlen = sim_convert_seed (values, count, false, type, seed.str)) != 0)
goto done;
values[i + 1] = values[i];
values[i] = j;
}
memset (seed.str, 0, (count * KEY_SEED_WORD_BITS + 7) / 8 + 2);
}
done:
memset (tmp, 0, sizeof (tmp));
memset (values, 0, sizeof (values));
memset (words, 0, sizeof (words));
for (; count--; sim_free (distances[count], (1 << KEY_SEED_WORD_BITS) * sizeof (*distances[count]))) {}
string_buffer_free (ascii);
key_unload_countries ();
return string_buffer_truncate (seed, seedlen);
}
void random_open_seed (simrandom generator, const simtype seed, const simtype entropy, int part, int parts) {
unsigned len = seed.len / parts + (part == parts ? seed.len % parts : 0), keysize;
simbyte *buf = seed.str + (part - 1) * len;
int i = -3;
const char *cipher;
simtype seeds = table_new (2), hash;
/*LOG_XTRA_SIMTYPE_ (pointer_new_len (buf, len), LOG_BIT_BIN, "seed ");*/
while ((cipher = sim_crypt_cipher_get (++i, &keysize, NULL)) != NULL)
if (i != 1 && keysize >= RANDOM_SIZE_BLOCK * 2) {
unsigned blocksize = i < 0 ? RANDOM_SIZE_HASH : RANDOM_SIZE_BLOCK;
simtype padded = string_buffer_new (i < 0 ? RANDOM_SIZE_HASH + len : RANDOM_SIZE_BLOCK + (len + 7) / 8 * 8);
void *md = sim_crypt_md_new (CRYPT_MD_SHA2);
if (entropy.typ != SIMNIL) {
hash = sim_crypt_md_hash (md, nil (), nil (), pointer_new_len (buf + RANDOM_SIZE_HASH, len - RANDOM_SIZE_HASH));
} else if (len >= keysize + blocksize) {
hash = sim_crypt_md_hash (md, nil (), nil (), pointer_new_len (buf + keysize, len - keysize));
} else
hash = sim_crypt_md_hash (md, nil (), nil (), pointer_new_len (buf, len));
if (hash.len < blocksize) { /* impossible */
LOG_FATAL_ (SIM_OK, "random %s %u < %u\n", cipher, hash.len, blocksize);
return;
}
memset (padded.str, 0, padded.len);
memcpy (padded.str, hash.str, blocksize);
memcpy (padded.str + blocksize, buf, len);
string_free (hash);
keysize += blocksize;
/*LOG_XTRA_SIMTYPES_ (pointer_new_len (padded.str, blocksize),
pointer_new_len (padded.str + blocksize,
(keysize < padded.len ? keysize : padded.len) - blocksize),
LOG_BIT_BIN, "seed %s ", cipher);*/
table_add (seeds, cipher, string_buffer_truncate (padded, keysize < padded.len ? keysize : padded.len));
}
random_open (generator, seeds, entropy.str, entropy.len);
table_free (seeds);
}
static unsigned sim_random_buffer_debias (const simbyte *input, unsigned length, simbyte *output) {
int bits = 8, byt;
simbyte *buf = output;
while (length--) /* debias bits like Neumann Janos */
for (byt = *input++; byt; byt >>= 2) {
int bit = byt & 3;
if (bit == 1 || bit == 2) {
if (bits == 8)
bits = *output++ = 0;
output[-1] |= (simbyte) ((bit & 1) << bits++);
}
}
return output - buf;
}
void random_buffer_init (simrandom generator) {
random_buffers[RANDOM_CASE_INDEX (generator)] = nil ();
}
static simbool random_buffer_check_entropy (simrandom generator, unsigned parts) {
int i = RANDOM_CASE_INDEX (generator);
if (random_bytes[i] >= RANDOM_SIZE_ENTROPY * RANDOM_ENTROPY_FACTOR * parts)
if (random_bits[i] >= RANDOM_SIZE_ENTROPY * 8 * RANDOM_ENTROPY_FACTOR * parts)
return true;
#ifdef DONOT_DEFINE /* fill entropy buffer with zeros - do not ever enable except for testing */
string_buffer_append (&random_buffers[i], random_bytes[i],
RANDOM_SIZE_ENTROPY * RANDOM_ENTROPY_FACTOR * parts - random_bytes[i]);
random_bits[i] = (random_bytes[i] = RANDOM_SIZE_ENTROPY * RANDOM_ENTROPY_FACTOR * parts) * 8;
return true;
#else
return false;
#endif
}
unsigned random_buffer_add_bits (simrandom generator, int bits, unsigned *bytes) {
int i = RANDOM_CASE_INDEX (generator);
if (bytes)
*bytes = random_bytes[i];
return random_bits[i] += bits;
}
void random_buffer_add (simrandom generator, const void *input, unsigned length, unsigned maxlen) {
int i = RANDOM_CASE_INDEX (generator);
string_buffer_append (&random_buffers[i], random_bytes[i], maxlen);
random_bytes[i] += sim_random_buffer_debias (input, length, random_buffers[i].str + random_bytes[i]);
}
void random_buffer_xor (simrandom generator, const simbyte *input, unsigned length) {
int i = RANDOM_CASE_INDEX (generator);
unsigned l = length, len = random_bytes[i], done = l && len ? 0 : 3;
while (done < 3) {
random_buffers[i].str[--len] ^= *input++;
if (! --l) {
done |= 1;
input -= (l = length);
}
if (! len) {
done |= 2;
len = random_bytes[i];
}
}
}
simbool random_buffer_open (simrandom generator, unsigned part, unsigned parts) {
void **rng = generator == random_seeded ? random_private : generator;
if (! random_buffer_check_entropy (rng, parts))
return false;
if (part) {
unsigned size;
int i = RANDOM_CASE_INDEX (rng);
simbyte *buf = random_buffers[i].str + (part - 1) * (size = random_bytes[i] / parts);
simtype saved = nil ();
if (part == parts)
size += random_bytes[i] % parts;
if (generator == random_session)
saved = random_get_state (generator);
random_open (generator, saved, buf, size);
if (generator == random_session)
table_free (saved);
memset (buf, 0, size);
if (part == parts)
random_buffer_close (rng);
}
return true;
}
void random_buffer_close (simrandom generator) {
int i = RANDOM_CASE_INDEX (generator);
random_bits[i] = random_bytes[i] = 0;
if (random_buffers[i].ptr)
string_buffer_free (random_buffers[i]);
random_buffers[i] = nil ();
}
static void key_time_start (struct key_time *timer) {
timer->cputime = sim_system_cpu_get (SYSTEM_CPU_TIME_THREAD, NULL);
timer->realtime = system_get_tick ();
}
static int key_time_stop (struct key_time *timer, int factor) {
int max = param_get_min ("socket.recv", 0);
struct key_time now;
key_time_start (&now);
timer->cputime = (now.cputime - timer->cputime) / 1000;
timer->realtime = (now.realtime - timer->realtime) * 1000;
if (timer->cputime >= max * factor * 1000000)
return SIM_KEY_TOO_LARGE;
if (timer->realtime >= max * factor * 1000000)
return SIM_KEY_TOO_SLOW;
return SIM_OK;
}
simtype key_get (simbool myself, simtype *ec, simtype *rsa) {
if (! myself) {
*ec = pointer_new_len (key_system_ec, key_system_ec_size);
*rsa = pointer_new_len (key_system_rsa, key_system_rsa_size);
} else {
if (! key_table.len)
return *rsa = *ec = nil ();
*ec = table_get_string (key_table, KEY_EC);
*rsa = table_get_string (key_table, KEY_RSA);
}
return key_file.ptr ? table_get_string (key_file, KEY_SEED) : nil ();
}
static int sim_key_set__ (const simtype eckey, const simtype rsakey, simtype *key, simtype *address) {
int err = SIM_SSL_ERROR;
void *keys[2] = { NULL, NULL };
if ((keys[SSL_KEY_EC] = sim_ssl_new_key__ (eckey, SSL_KEY_EC, false)) == NULL) {
LOG_ERROR_ ("failed to load EC key\n");
} else if ((keys[SSL_KEY_RSA] = sim_ssl_new_key__ (rsakey, SSL_KEY_RSA, false)) == NULL) {
LOG_ERROR_ ("failed to load RSA key\n");
} else if (address) {
simtype ec = sim_ssl_convert_key__ (keys[SSL_KEY_EC], SSL_KEY_EC);
simtype rsa = sim_ssl_convert_key__ (keys[SSL_KEY_RSA], SSL_KEY_RSA);
if (ec.typ != SIMNIL && rsa.typ != SIMNIL) {
table_add (key_table = table_new (2), KEY_EC, ec);
table_add (key_table, KEY_RSA, rsa);
*key = sim_crypt_md_hash_address (sim_crypt_md_new (CRYPT_MD_SHA), sim_crypt_md_new (CRYPT_MD_RIPEMD), ec, rsa);
*address = sim_contact_convert_to_address (*key, 'S');
LOG_XTRA_ ("address = %s\n", address->str);
LOG_INFO_ ("id = @%020llu\n", sim_contact_convert_to_id (address->ptr, *key));
STRING_FREE (key, string_new (eckey.len + rsakey.len));
memcpy (key->str, eckey.str, eckey.len);
memcpy (key->str + eckey.len, rsakey.str, rsakey.len);
err = SIM_OK;
} else
LOG_ERROR_ ("%s public key is missing\n", ec.typ == SIMNIL ? "EC" : "RSA");
} else
err = sim_ssl_set_keys__ (keys[SSL_KEY_EC], keys[SSL_KEY_RSA]);
sim_ssl_free_key__ (keys[SSL_KEY_EC]);
sim_ssl_free_key__ (keys[SSL_KEY_RSA]);
return err;
}
static int key_set_ (const simtype key, simtype *address) {
int err = SIM_KEY_NO_PRIVATE;
simtype eckey = table_get_string (key, KEY_EC), rsakey = table_get_string (key, KEY_RSA);
if (eckey.typ != SIMNIL && rsakey.typ != SIMNIL) {
simtype tmp = nil ();
if (address)
random_open_seed (random_session, rsakey, nil (), 1, 1);
PTH_UNPROTECT_PROTECT_ (err = sim_key_set__ (eckey, rsakey, &tmp, address));
if (address)
random_close (random_session);
if (tmp.typ != SIMNIL)
file_set_password (tmp, false);
} else
LOG_ERROR_ ("%s private key is missing\n", eckey.typ == SIMNIL ? "EC" : "RSA");
return err;
}
simtype key_generate_seed (int size) {
simtype seed = string_new ((size + 7) / 8);
int parts = (seed.len - 1) / RANDOM_SIZE_BLOCK + 1, part;
for (part = 1; part <= parts; part++) {
unsigned len = part == parts ? seed.len - (part - 1) * RANDOM_SIZE_BLOCK : RANDOM_SIZE_BLOCK;
if (! random_buffer_open (random_private, part, parts)) {
string_free (seed);
return nil ();
}
random_get (random_private, pointer_new_len (seed.str + (part - 1) * RANDOM_SIZE_BLOCK, len));
random_close (random_private);
}
return seed;
}
static int key_generate_ (simnumber *mseconds, const simtype seed, int size, simtype *address) {
int err = SIM_KEY_NO_ENTROPY;
key_uninit ();
key_file = table_new (2);
if (seed.len) {
unsigned minbytes = size > 8192 ? 32 : size > 4096 ? 24 : 16;
simbool triple = seed.len >= minbytes * 3;
if (seed.len >= minbytes * 2) {
random_open_seed (random_private, seed, nil (), 1, triple ? 3 : 2);
random_open_seed (random_seeded, seed, nil (), 2, triple ? 3 : 2);
table_add (key_file, KEY_RSA, random_new_rsa_ (random_seeded, mseconds, size));
random_close (random_seeded);
random_open_seed (random_private, seed, nil (), triple ? 3 : 1, triple ? 3 : 1);
} else {
random_open_seed (random_private, seed, nil (), 1, 1);
table_add (key_file, KEY_RSA, random_new_rsa_ (random_private, mseconds, size));
}
goto ec;
} else if (random_buffer_open (random_private, 1, 3) && random_buffer_open (random_seeded, 2, 3)) {
table_add (key_file, KEY_RSA, random_new_rsa_ (random_seeded, mseconds, size));
random_close (random_seeded);
if (random_buffer_open (random_private, 3, 3)) {
ec:
table_add (key_file, KEY_EC, random_new_ec_ (random_private, false));
err = SIM_OK;
}
random_close (random_private);
}
if (err == SIM_OK)
err = key_set_ (key_file, address);
if (err != SIM_OK)
key_uninit ();
return err;
}
static int event_send_keygen_ (int error, unsigned type, simnumber *seconds) {
struct key_time timer;
if (type != SIM_KEY_NONE) {
simbyte zero[RANDOM_SIZE_BLOCK];
memset (zero, 0, sizeof (zero));
random_open_seed (random_public, pointer_new_len (&zero, sizeof (zero)), nil (), 1, 1);
key_time_start (&timer);
random_test_key_ (random_public, pointer_new_len (key_pointers[type], *key_lengths[type]), 2, SSL_KEY_RSA);
random_close (random_public);
}
if (type != SIM_KEY_NONE && (error = key_time_stop (&timer, 2)) != SIM_KEY_TOO_LARGE) {
int factor = (11 - type) << 4; /* adjust validation time for generation of two random primes */
*seconds = timer.realtime * ((SSL_RSA_BITS_MIN << type) + 3 * factor) / (factor * 1000000) + 1;
event_send_value_number (NULL, SIM_EVENT_KEYGEN, SIM_EVENT_KEYGEN_TIME, *seconds);
error = SIM_OK;
} else
event_send_value_number (NULL, SIM_EVENT_KEYGEN, SIM_EVENT_KEYGEN, error);
return error;
}
static void *thread_keygen_ (void *arg) {
int err;
unsigned *decrement = ((struct keygen_arg *) arg)->state, type = ((struct keygen_arg *) arg)->type;
simtype key = ((struct keygen_arg *) arg)->key, seed = ((struct keygen_arg *) arg)->seed, addr = nil ();
simnumber sec = 0, start = system_get_tick ();
LOG_API_DEBUG_ ("%d %s\n", type, key.len ? "SEED" : (char *) key.str);
if ((err = event_send_keygen_ (SIM_OK, type, &sec)) == SIM_OK) {
simnumber tick = system_get_tick (), estimated = sec * 1000 / 2 - (tick - start) * 3;
if ((err = key_generate_ (&estimated, seed, SSL_RSA_BITS_MIN << type, &addr)) == SIM_OK) {
simtype myaddr = sim_contact_convert_address (addr.ptr, 'S'), event = table_new_name (2, SIM_EVENT_KEYGEN);
if ((sec = (simnumber) (system_get_tick () - tick + (tick - start) * 6 - estimated) / 1000 - sec + 1) < 0) {
event_send_value_number (NULL, SIM_EVENT_KEYGEN, SIM_EVENT_KEYGEN_TIME, sec);
} else
LOG_WARN_ ("generate time exceeded by %lld seconds\n", sec);
if (seed.typ == SIMNIL && key.typ != SIMNIL) {
file_set_password (key.len ? key : nil (), true);
random_init_entropy (myaddr);
random_init (random_public); /* cannot fail */
err = key_save_ (KEY_MODE_MAKE | KEY_MODE_SAVE);
key_uninit ();
file_set_password (key = nil (), true);
} else {
simtype hash = sim_crypt_md_hash (sim_crypt_md_new (CRYPT_MD_RIPEMD), nil (), nil (), seed);
table_add (key_file, seed.typ != SIMNIL ? KEY_SEED : KEY_NO_SEED, hash);
random_open_seed (random_public, myaddr, nil (), 1, 1);
err = key_save_ (KEY_MODE_MAKE);
}
random_close (random_public);
table_add_number (event, SIM_EVENT_KEYGEN, err);
table_add (event, SIM_EVENT_KEYGEN_ADDRESS, addr);
event_send_id (sim_contact_convert_to_id (addr.ptr, myaddr), event);
table_free (event);
string_free (myaddr);
} else
event_send_keygen_ (err, SIM_KEY_NONE, NULL);
}
param_close ();
string_free (seed);
string_free (key);
sim_free (arg, sizeof (struct keygen_arg));
LOG_API_WARN_ (err);
if (err == SIM_OK)
LOG_API_DEBUG_ ("\n");
(*decrement)--; /* api_init_state = API_INITIALIZED; */
return pth_thread_exit_ (false);
}
static simnumber sim_key_test_cipher (int cipher, const simbyte *input, unsigned length, simbool encrypt,
simbyte *output) {
unsigned i;
void *crypt;
simnumber identifier = 0;
simtype iv;
struct _ssl_master master;
memset (&master, 0, sizeof (master));
master.length = sizeof (master.premaster) / 2;
master.to = pointer_new (PROXY_ADDRESS_CONTROL);
iv = string_buffer_new (sim_crypt_auth_size (crypt = sim_crypt_open (cipher, &master, 0, encrypt)));
memset (iv.str, 0, iv.len);
sim_crypt_auth_start (crypt, length, iv.str, iv.len, 0);
if (! encrypt) {
sim_crypt_auth_update (crypt, input, length);
sim_crypt_decrypt (crypt, pointer_new_len (input, length), output);
} else
sim_crypt_encrypt (crypt, pointer_new_len (input, length), output);
sim_crypt_auth_stop (crypt, iv.str);
sim_crypt_free (crypt, encrypt);
for (i = 0; i < sizeof (identifier); i++)
identifier = identifier << 8 | iv.str[i];
string_buffer_free (iv);
return identifier;
}
static int key_test (void) {
static const simbyte key_test_data[2][RANDOM_SIZE_BLOCK] = {
{ 0xF2, 0x76, 0x0C, 0x89, 0x48, 0x7A, 0x4B, 0xF0, 0xD4, 0x7F, 0x6C, 0xCC, 0xA8, 0xD6, 0x89, 0x15 },
{ 0x71, 0xB2, 0x93, 0xBB, 0x98, 0xFE, 0x94, 0xC8, 0xDA, 0xB9, 0x4B, 0x45, 0xC4, 0xF9, 0x5A, 0x53 }
};
int err = SIM_OK, i = -1;
const char *cipher;
simnumber identifier, identifier1, identifier2;
simbyte seed[RANDOM_SIZE_BLOCK], tmp[RANDOM_SIZE_BLOCK];
simtype seedptr;
while ((cipher = sim_crypt_cipher_get (++i, NULL, &identifier)) != NULL) {
memset (tmp, 0, sizeof (tmp));
memset (seed, 0, sizeof (seed));
identifier1 = sim_key_test_cipher (i, seed, sizeof (seed), true, tmp) & 0x3FFFFFFFFFFFFFFFLL;
memset (seed, 0, sizeof (seed));
identifier2 = sim_key_test_cipher (i, seed, sizeof (seed), true, seed) & 0x3FFFFFFFFFFFFFFFLL;
if (identifier1 != identifier) {
LOG_ERROR_ ("test-%s: %lld\n", cipher, identifier1);
err = SIM_CRYPT_BAD_CIPHER;
} else if (identifier2 != identifier || memcmp (seed, tmp, sizeof (seed))) {
LOG_ERROR_ ("TEST-%s: %lld\n", cipher, identifier2);
err = SIM_CRYPT_BAD_CIPHER;
} else {
identifier1 = sim_key_test_cipher (i, seed, sizeof (seed), false, seed) & 0x3FFFFFFFFFFFFFFFLL;
memset (tmp, 0, sizeof (tmp));
if (identifier1 != identifier || memcmp (seed, tmp, sizeof (tmp))) {
LOG_ERROR_ ("Test-%s: %lld\n", cipher, identifier1);
err = SIM_CRYPT_BAD_CIPHER;
}
}
}
for (i = 0; i <= 1; i++) {
memset ((seedptr = pointer_new_len (seed, sizeof (seed))).ptr, 0, sizeof (seed));
if (i) {
random_open_seed (random_private, seedptr, nil (), 1, 1);
random_get (random_private, pointer_new_len (seed, sizeof (seed)));
random_close (random_private);
} else {
simtype hash = sim_crypt_md_hash (sim_crypt_md_new (CRYPT_MD_RIPEMD), nil (), nil (), seedptr);
memcpy (seed, hash.str, sizeof (seed));
string_free (hash);
}
if (memcmp (seed, key_test_data[i], sizeof (seed))) {
LOG_ERROR_SIMTYPE_ (seedptr, LOG_BIT_BIN, "test-%s: ", i ? "rng" : "ripemd");
err = SIM_CRYPT_BAD_CIPHER;
}
}
return err;
}
int key_generate_test_ (void) {
simtype addr, key, seedptr;
simbyte seed[RANDOM_SIZE_BLOCK];
int err = key_test (), i;
memset (seed, 0, sizeof (seed));
seedptr = pointer_new_len (seed, sizeof (seed));
if (err == SIM_OK)
for (i = 0; (unsigned) i < SIM_ARRAY_SIZE (key_pointers); i++) {
simnumber zero = 0;
if ((err = key_generate_ (&zero, seedptr, SSL_RSA_BITS_MIN << i, &addr)) != SIM_OK)
break;
string_free (addr);
if (! i && string_check_diff_len (key = table_get_string (key_file, KEY_EC), key_ec, key_ec_size)) {
LOG_ERROR_SIMTYPE_ (key, LOG_BIT_BIN, "test-ec: ");
err = SIM_CRYPT_BAD_CIPHER;
}
if (string_check_diff_len (key = table_get_string (key_file, KEY_RSA), key_pointers[i], *key_lengths[i])) {
LOG_ERROR_SIMTYPE_ (key, LOG_BIT_BIN, "test-%d: ", SSL_RSA_BITS_MIN << i);
err = SIM_CRYPT_BAD_CIPHER;
}
key_uninit ();
}
return err;
}
int key_generate_key (unsigned *decrement, simtype password, simtype seed, unsigned type) {
int err = SIM_KEY_NO_ENTROPY;
struct keygen_arg *arg = sim_new (sizeof (*arg));
arg->state = decrement;
arg->type = type;
arg->key = password;
arg->seed = seed;
if (seed.len || random_buffer_check_entropy (random_private, 3))
err = pth_thread_spawn (thread_keygen_, arg, NULL, NULL, -1);
if (err != SIM_OK) {
string_free (arg->seed);
string_free (arg->key);
sim_free (arg, sizeof (*arg));
}
return err;
}
static int key_validate_ (int level) {
int err = SIM_KEY_BAD_KEY;
struct key_time timer;
simtype saved = random_get_state (random_public);
key_time_start (&timer);
if (! random_test_key_ (random_public, table_get_string (key_file, KEY_EC), level, SSL_KEY_EC)) {
LOG_ERROR_ ("failed to validate EC key\n");
} else if (! random_test_key_ (random_public, table_get_string (key_file, KEY_RSA), level, SSL_KEY_RSA)) {
LOG_ERROR_ ("failed to validate RSA key\n");
} else if ((err = key_time_stop (&timer, level >= 3 ? 9 : 3)) == SIM_KEY_TOO_SLOW) {
LOG_ERROR_ ("Your computer is too busy to use a %d-bit RSA key\n", CONTACT_GET_KEY_SIZE (contact_list.me));
err = SIM_OK;
}
random_open (random_public, saved, (simbyte *) "", 0);
table_free (saved);
return err;
}
int key_save_ (int mode) {
int err = SIM_OK, err2 = SIM_KEY_NO_SAVE, level = 3;
simtype revoked = table_get_string (key_file, KEY_REVOKED);
if (mode == KEY_MODE_KILL) {
simtype name = sim_file_name_new (FILE_KEY, FILE_DIRECTORY_USER);
simtype bak = sim_file_name_new (FILE_KEY SIM_FILE_BACKUP, FILE_DIRECTORY_USER);
if (name.typ != SIMNIL) {
err = file_wipe (NULL, name.ptr, sim_file_write);
err2 = file_wipe (NULL, bak.ptr, sim_file_write);
}
string_free (bak);
string_free (name);
return err2 == SIM_OK ? err : err2;
}
if (revoked.typ != SIMNIL) {
mode = KEY_MODE_LOAD;
} else if ((mode == KEY_MODE_LOAD && (level = param_get_number ("crypto.validate")) >= 0) || mode & KEY_MODE_MAKE) {
if (mode == KEY_MODE_LOAD && level > 1)
event_send_tick_type (SIM_EVENT_KEYGEN);
if ((err = key_validate_ (level)) != SIM_OK && level > 1 && key_validate_ (1) != SIM_OK && mode == KEY_MODE_LOAD)
param_set_number ("crypto.validate", 1, SIM_PARAM_TEMPORARY);
}
if (mode & KEY_MODE_SAVE && err == SIM_OK) {
if (key_file.ptr) {
if (mode & KEY_MODE_LOAD)
if (sim_file_check_exists (FILE_KEY) || sim_file_check_exists (FILE_KEY SIM_FILE_BACKUP))
err = SIM_KEY_EXISTS;
if (err == SIM_OK)
err = file_save (key_file, FILE_KEY, FILE_TYPE_ENCRYPTED | FILE_TYPE_KEY | FILE_TYPE_TEMPORARY);
if (err == SIM_OK) {
file_copy (FILE_KEY, FILE_KEY SIM_FILE_BACKUP);
if (! (mode & KEY_MODE_MAKE) && (err != contact_list_save ()) != SIM_OK) {
event_send_name_number (NULL, SIM_EVENT_ERROR, SIM_EVENT_ERROR_FILE_SAVE, err);
err = SIM_OK;
}
}
} else
err = SIM_KEY_NO_PRIVATE;
}
if (revoked.typ != SIMNIL) {
err = SIM_KEY_NOT_SECURE;
} else if (err == SIM_OK && mode == KEY_MODE_LOAD) {
simtype ciphers = string_copy_string (param_get_string ("ssl.ciphers"));
simtype curves = array_copy_strings (param_get_strings ("ssl.curves"));
simtype sigalgs = array_copy_strings (param_get_strings ("ssl.sigalgs"));
PTH_UNPROTECT (ssl_uninit_ (true));
err = ssl_init_ (ciphers.ptr, curves, sigalgs);
if (PTH_PROTECT_ (err) == SIM_OK && (err = key_set_ (key_file, NULL)) == SIM_OK)
err = ssl_reinit_ ();
if (err != SIM_OK)
LOG_ERROR_ ("ssl init: %s\n", convert_error (err, true));
array_free (sigalgs);
array_free (curves);
string_free (ciphers);
}
return err;
}
int key_init_ (const char *password) {
int err, err2 = SIM_OK;
simtype key = nil (), key2, addr = nil ();
#ifndef DONOT_DEFINE
if ((err = key_test ()) != SIM_OK)
#else /* generate keytest.h */
if ((err = key_generate_test_ ()) != SIM_OK)
#endif
return err;
if (password) {
simtype seed = file_set_password (*password ? string_copy (password) : nil (), true);
key_uninit ();
err2 = file_load (FILE_KEY SIM_FILE_BACKUP, FILE_TYPE_ENCRYPTED | FILE_TYPE_KEY | FILE_TYPE_TEMPORARY, &key2);
if ((err = file_load (FILE_KEY, FILE_TYPE_ENCRYPTED | FILE_TYPE_KEY | FILE_TYPE_TEMPORARY, &key)) != SIM_OK) {
if (err2 != SIM_OK) {
if (err != SIM_KEY_NO_KEY)
return err;
if (err2 != SIM_KEY_NO_KEY) {
sim_error_set_text (" [", FILE_KEY, "]", err2);
} else if (sim_file_check_exists (FILE_CONTACTS) || sim_file_check_exists (FILE_CONTACTS SIM_FILE_BACKUP))
return SIM_KEY_NO_SAVED;
return err2;
}
file_copy (FILE_KEY SIM_FILE_BACKUP, FILE_KEY);
key = key2;
} else if (err2 != SIM_OK) {
file_copy (FILE_KEY, FILE_KEY SIM_FILE_BACKUP);
} else
table_free (key2);
if ((err = key_set_ (key, &addr)) == SIM_OK) {
simtype hash = sim_crypt_md_hash (sim_crypt_md_new (CRYPT_MD_RIPEMD), nil (), nil (), seed);
table_add (key, seed.typ != SIMNIL ? KEY_SEED : KEY_NO_SEED, hash);
key_file = key;
}
err2 = SIM_OK;
} else if (key_file.ptr) {
TABLE_FREE (&key_table, nil ());
if ((err = key_set_ (key_file, &addr)) == SIM_OK && file_load (FILE_KEY, FILE_TYPE_KEY, &key2) == SIM_OK) {
simtype seed = table_get_string (key_file, KEY_SEED);
if (string_check_diff_len (table_get_string (key2, KEY_SEED), seed.str, seed.len))
err = SIM_KEY_EXISTS;
table_free (key2);
}
err2 = SIM_KEY_EXISTS;
} else
err = SIM_KEY_NO_PRIVATE;
if (err == SIM_OK) {
if ((err = contact_list_load (key = sim_contact_convert_address (addr.ptr, 'S'))) != SIM_OK) {
if (err2 != SIM_OK)
err = err2;
} else if ((err = sim_const_init ()) == SIM_OK) {
if (! contact_list.array.len) {
contact_new_ (addr.ptr, CONTACT_AUTH_SELF, &contact_list.me);
if (contact_list.me)
contact_list.me->flags |= CONTACT_FLAG_SOUND_ON_Y | CONTACT_FLAG_SOUND_OFF_Y;
} else if (! string_check_diff (addr, ((simcontact) contact_list.array.arr[1].ptr)->addr)) {
contact_list.me = contact_list.array.arr[1].ptr;
contact_list.auth = contact_list.me->auth;
contact_list.me->auth = CONTACT_AUTH_ACCEPTED;
} else
err = SIM_KEY_NO_CONTACT;
if (contact_list.me) {
const char *os = sim_system_get_version (NULL, NULL);
contact_list.me->flags |= CONTACT_FLAG_VERIFY | CONTACT_FLAG_VERIFIED |
CONTACT_FLAG_UTF | CONTACT_FLAG_XFER_N | CONTACT_FLAG_AUDIO_N;
contact_list.me->flags &= ~(CONTACT_FLAG_XFER | CONTACT_FLAG_AUDIO | CONTACT_FLAG_EDIT_N |
CONTACT_FLAG_XFER_Y | CONTACT_FLAG_AUDIO_Y | CONTACT_FLAG_EDIT_Y);
if (os)
event_send_name (contact_list.me, SIM_EVENT_ERROR, SIM_EVENT_ERROR_SYSTEM, pointer_new (os));
} else if (err == SIM_OK)
err = SIM_CONTACT_BAD_ID;
if (err == SIM_OK && contact_new_ (CONTACT_ADDRESS_SYSTEM, CONTACT_AUTH_SYSTEM, &contact_list.system) == SIM_OK) {
contact_list.system->flags &= ~CONTACT_FLAG_AUDIO;
STRING_FREE (&contact_list.system->nick, contact_list.system->own = pointer_new (CONTACT_VIP_SYSTEM));
}
}
string_free (key);
} else
table_free (key);
string_free (addr);
return err;
}
void key_uninit (void) {
if (key_file.ptr)
table_free (key_file);
if (key_table.ptr)
table_free (key_table);
key_table = key_file = nil ();
}
void key_log_private (const char *module, int level) {
if (key_file.ptr)
log_simtype_ (module, level, key_file, LOG_BIT_BIN, "private keys: ");
}
void key_print_speed_ (unsigned seconds) {
const char *cipher = "speex-32000";
unsigned len = 0;
int i = -3, j;
simnumber cputime, count;
struct key_time timer;
simtype ec, rng = string_cat ("r-", random_get_cipher ());
static simbyte buf[16384];
do {
void *crypt = NULL;
simtype iv = nil (), key = string_buffer_new (len);
memset (key.str, 0, len);
memset (buf, 0, sizeof (buf));
if (i >= 0) {
iv = string_buffer_new (sim_crypt_auth_size (crypt = sim_crypt_new (i, key, true)));
memset (iv.str, 0, iv.len);
} else if (i > -3)
cipher = i != -1 ? "random-init" : string_check_diff (rng, "r-") ? rng.ptr : "random-gen";
printf ("%s... ", cipher);
fflush (stdout);
for (cputime = count = 0; cputime < seconds * 1000000; cputime += timer.cputime) {
int rate = 32000;
unsigned samples = 524288;
key_time_start (&timer);
if (i > -3) {
for (j = 64; j; j--)
if (i >= 0) {
sim_crypt_auth_encrypt (crypt, pointer_new_len (buf, sizeof (buf)), iv, 0, iv.str);
} else if (i == -1) {
random_get (random_public, pointer_new_len (buf, sizeof (buf)));
} else
random_open (random_public, nil (), buf, sizeof (buf));
} else
string_free (sim_sound_load ("hello", &rate, &samples));
key_time_stop (&timer, 1);
if (! samples)
break;
count += samples * 2;
}
sim_crypt_free (crypt, true);
string_buffer_free (iv);
string_buffer_free (key);
printf ("%s\t%g MB/sec\n", strlen (cipher) > 3 ? "" : "\t", cputime ? (double) count / cputime : 0);
fflush (stdout);
} while ((cipher = sim_crypt_cipher_get (++i, &len, NULL)) != NULL);
random_open_seed (random_session, pointer_new_len (buf, RANDOM_SIZE_BLOCK), nil (), 1, 1);
ec = random_new_ec_ (random_public, true);
for (i = -2; i < (int) SIM_ARRAY_SIZE (key_pointers); i++) {
pth_unprotect ();
if (i >= 0) {
void *rsa;
simtype encrypted, pub = nil (), buffer;
printf ("rsa-%d... ", SSL_RSA_BITS_MIN << i);
fflush (stdout);
rsa = sim_ssl_new_key__ (pointer_new_len (key_pointers[i], *key_lengths[i]), SSL_KEY_RSA, false);
if (sim_ssl_set_keys__ (NULL, rsa) == SIM_OK)
pub = sim_ssl_convert_key__ (rsa, SSL_KEY_RSA);
sim_ssl_free_key__ (rsa);
pth_protect_ ();
random_get (random_public, buffer = string_buffer_new (ssl_rsa_size_ (pub)));
encrypted = ssl_rsa_crypt_public_ (buffer, pub, nil ());
string_buffer_free (buffer);
string_free (pub);
for (cputime = count = 0; cputime < seconds * 1000000; cputime += timer.cputime) {
key_time_start (&timer);
string_free (ssl_rsa_crypt_private_ (encrypted, true));
key_time_stop (&timer, 1);
count += 2000000;
}
string_free (encrypted);
} else {
printf ("ec-%d... ", i == -1 ? 512 : 256);
fflush (stdout);
for (cputime = count = 0; cputime < seconds * 1000000; cputime += timer.cputime) {
key_time_start (&timer);
sim_ssl_free_key__ (sim_ssl_new_key__ (ec, i == -1 ? SSL_KEY_EC : SSL_KEY_ANONYMOUS, true));
key_time_stop (&timer, 1);
count += 2000000;
}
pth_protect_ ();
}
printf ("\t%g modexp/sec\n", cputime ? (double) count / cputime : 0);
fflush (stdout);
STRING_FREE (&ec, i == -2 ? pointer_new_len (key_ec, key_ec_size) : nil ());
}
random_close (random_session);
random_close (random_public);
string_free (rng);
}