/* nettle.cmod -*- c -*- */ |
|
#include "global.h" |
#include "interpret.h" |
#include "svalue.h" |
/* For this_object() */ |
#include "object.h" |
#include "operators.h" |
#include "module_support.h" |
#include "threads.h" |
#include "pike_memory.h" |
|
#include "nettle_config.h" |
|
#ifdef HAVE_LIBNETTLE |
|
#include "nettle.h" |
|
#include <nettle/yarrow.h> |
#include <nettle/knuth-lfib.h> |
|
#include <stdio.h> |
#include <stdarg.h> |
|
DECLARATIONS |
|
/*! @module Nettle |
*! Low level crypto functions used by the @[Crypto] module. Unless |
*! you are doing something very special, you would want to use the |
*! Crypto module instead. |
*/ |
|
/*! @class Yarrow |
*! |
*! Yarrow is a family of pseudo-randomness generators, designed for |
*! cryptographic use, by John Kelsey, Bruce Schneier and Niels Ferguson. |
*! Yarrow-160 is described in a paper at |
*! @url{http://www.schneier.com/paper-yarrow.html@}, and it uses SHA1 and |
*! triple-DES, and has a 160-bit internal state. Nettle implements |
*! Yarrow-256, which is similar, but uses SHA256 and AES to get an |
*! internal state of 256 bits. |
*/ |
PIKECLASS Yarrow |
{ |
CVAR struct yarrow256_ctx ctx; |
CVAR struct yarrow_source *sources; |
|
#ifndef HAVE_STRUCT_YARROW256_CTX_SEED_FILE |
/* NOTE: Nettle 2.0 does not have the automatic seed_file maintenance |
* that Nettle 1.x had. This stuff is needed since it affected |
* the state emitted by random_string(). When Nettle 2.0 is the |
* default, consider implementing this via overloading of the |
* various seeding functions instead, since it does have a bit |
* of overhead. |
* |
* /grubba 2009-07-05 |
*/ |
PIKEVAR string seed_file flags ID_PRIVATE|ID_STATIC; |
#endif |
|
DECLARE_STORAGE; |
|
#ifndef HAVE_STRUCT_YARROW256_CTX_SEED_FILE |
static void pike_generate_seed_file(void) |
{ |
struct pike_string *seed_file = |
begin_shared_string(YARROW256_SEED_FILE_SIZE); |
yarrow256_random(&THIS->ctx, YARROW256_SEED_FILE_SIZE, STR0(seed_file)); |
if (THIS->seed_file) { |
free_string(THIS->seed_file); |
} |
THIS->seed_file = end_shared_string(seed_file); |
} |
#else |
#define pike_generate_seed_file() |
#endif |
|
/*! @decl void create(void|int sources) |
*! The number of entropy sources that will feed entropy to the |
*! random number generator is given as an argument to Yarrow |
*! during instantiation. |
*! @seealso |
*! @[update] |
*/ |
PIKEFUN void create(void|int arg) |
flags ID_PROTECTED; |
{ |
INT32 num = 0; |
|
if(arg) { |
if (TYPEOF(*arg) != PIKE_T_INT) |
Pike_error("Bad argument type.\n"); |
num = arg->u.integer; |
if(num < 0) |
Pike_error("Invalid number of sources.\n"); |
free (THIS->sources); |
THIS->sources = xalloc(sizeof(struct yarrow_source)*num); |
} |
else |
{ |
free (THIS->sources); |
THIS->sources = NULL; |
} |
yarrow256_init(&THIS->ctx, num, THIS->sources); |
} |
|
/*! @decl Yarrow seed(string(0..255) data) |
*! |
*! The random generator needs to be seeded before it can be used. |
*! The seed must be at least 32 characters long. The seed could be |
*! stored from a previous run by inserting the value returned from |
*! @[get_seed]. |
*! |
*! @returns |
*! Returns the called object. |
*! @seealso |
*! @[min_seed_size], @[get_seed], @[is_seeded] |
*/ |
PIKEFUN object seed(string(0..255) data) |
optflags OPT_SIDE_EFFECT; |
{ |
if(data->len < YARROW256_SEED_FILE_SIZE) |
Pike_error("Seed must be at least %d characters.\n", |
YARROW256_SEED_FILE_SIZE); |
|
NO_WIDE_STRING(data); |
yarrow256_seed(&THIS->ctx, data->len, STR0(data)); |
pike_generate_seed_file(); |
RETURN this_object(); |
} |
|
/*! @decl int(0..) min_seed_size() |
*! Returns the minimal number of characters that the @[seed] |
*! needs to properly seed the random number generator. |
*! @seealso |
*! @[seed] |
*/ |
PIKEFUN int(0..) min_seed_size() |
optflags OPT_TRY_OPTIMIZE; |
{ |
RETURN YARROW256_SEED_FILE_SIZE; |
} |
|
/*! @decl string(0..255) get_seed() |
*! Returns part of the internal state so that it can |
*! be saved for later seeding. |
*! |
*! @seealso |
*! @[seed()], @[random_string()] |
*/ |
PIKEFUN string(0..255) get_seed() |
optflags OPT_EXTERNAL_DEPEND; |
rawtype tDeprecated(tFunc(tNone, tStr8)); |
{ |
if( !yarrow256_is_seeded(&THIS->ctx) ) |
Pike_error("Random generator not seeded.\n"); |
|
#ifdef HAVE_STRUCT_YARROW256_CTX_SEED_FILE |
RETURN make_shared_binary_string(THIS->ctx.seed_file, |
YARROW256_SEED_FILE_SIZE); |
#else |
if (THIS->seed_file) { |
REF_RETURN THIS->seed_file; |
} else { |
/* |
* It seems somewhat unreasonable to use uninitialized memory here. |
* Instead, I think the user should be warned. It really isnt a very |
* good source of entropy and may lead to undefined behavior in C. |
* Why not simply return 0 in that case? |
* /arne |
*/ |
struct pike_string *s = begin_shared_string(YARROW256_SEED_FILE_SIZE); |
PIKE_MEM_RW_RANGE(s->str, YARROW256_SEED_FILE_SIZE); |
s = end_shared_string(s); |
RETURN s; |
} |
#endif /* HAVE_STRUCT_YARROW256_CTX_SEED_FILE */ |
} |
|
/*! @decl int(0..1) is_seeded() |
*! Returns 1 if the random generator is seeded and ready |
*! to generator output. 0 otherwise. |
*! @seealso |
*! @[seed] |
*/ |
PIKEFUN int(0..1) is_seeded() |
optflags OPT_EXTERNAL_DEPEND; |
{ |
RETURN yarrow256_is_seeded(&THIS->ctx); |
} |
|
/*! @decl void force_reseed() |
*! By calling this function entropy is moved from the slow |
*! pool to the fast pool. Read more about Yarrow before using |
*! this. |
*/ |
PIKEFUN void force_reseed() |
optflags OPT_SIDE_EFFECT; |
{ |
#ifdef HAVE_NETTLE_YARROW256_SLOW_RESEED |
/* From change notes for Nettle 2.0: |
* |
* * Changes to the yarrow256 interface. The function |
* yarrow256_force_reseed has been replaced by the two |
* functions yarrow256_fast_reseed and yarrow256_slow_reseed, |
* which were previously static. |
*/ |
yarrow256_slow_reseed(&THIS->ctx); |
#else |
yarrow256_force_reseed(&THIS->ctx); |
#endif |
pike_generate_seed_file(); |
} |
|
/*! @decl int(0..1) update(string(0..255) data, int source, int entropy) |
*! Inject additional entropy into the random number generator. |
*! |
*! @seealso |
*! @[create] |
*/ |
PIKEFUN int(0..1) update(string(0..255) data, int source, int entropy) |
optflags OPT_SIDE_EFFECT; |
{ |
int ret; |
/* FIXME: Wide strings could actually be supported here */ |
NO_WIDE_STRING(data); |
if( !THIS->sources ) |
Pike_error("This random generator has no sources.\n"); |
if( source<0 || (unsigned)source>=THIS->ctx.nsources ) |
Pike_error("Invalid random source.\n"); |
if( entropy<0 ) |
Pike_error("Entropy must be positive.\n"); |
if( entropy>(data->len*8) ) |
Pike_error("Impossibly large entropy value.\n"); |
ret = yarrow256_update(&THIS->ctx, source, entropy, data->len, |
(const uint8_t *)data->str); |
if (ret) { |
pike_generate_seed_file(); |
} |
RETURN ret; |
} |
|
/*! @decl int(0..) needed_sources() |
*! The number of sources that must reach the threshold before a |
*! slow reseed will happen. |
*/ |
PIKEFUN int(0..) needed_sources() |
optflags OPT_EXTERNAL_DEPEND; |
{ |
RETURN yarrow256_needed_sources(&THIS->ctx); |
} |
|
/*! @decl string(0..255) random_string(int length) |
*! Returns a pseudo-random string of the requested @[length]. |
*/ |
PIKEFUN string(0..255) random_string(int length) |
optflags OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT; |
{ |
struct pike_string *rnd; |
if(length < 0) |
Pike_error("Invalid length, must be positive.\n"); |
if( !yarrow256_is_seeded(&THIS->ctx) ) |
Pike_error("Random generator not seeded.\n"); |
rnd = begin_shared_string(length); |
yarrow256_random(&THIS->ctx, length, (uint8_t *)rnd->str); |
RETURN end_shared_string(rnd); |
} |
|
INIT |
{ |
THIS->sources = NULL; |
yarrow256_init(&THIS->ctx, 0, NULL); |
} |
|
EXIT |
gc_trivial; |
{ |
if( THIS->sources ) |
{ |
free(THIS->sources); |
} |
} |
} |
|
/*! @endclass |
*/ |
|
#define INCREMENT(size, ctr) \ |
do { \ |
unsigned increment_i = (size) - 1; \ |
if (++(ctr)[increment_i] == 0) \ |
while (increment_i > 0 \ |
&& ++(ctr)[--increment_i] == 0 ) \ |
; \ |
} while (0) |
|
/*! @class Fortuna |
*! |
*! Implements the Fortuna PRNG generator, designed by Niels Ferguson and |
*! Bruce Schneier and described in Practical Cryptography. Web |
*! published exerpt at https://www.schneier.com:443/fortuna.pdf |
*! |
*! This implementation uses AES256 to generate output and SHA256 to |
*! generate keys. |
*! |
*! To use this class an entropy accumulator needs to be implemented |
*! and supply the @[reseed()] method with new entopy. |
*/ |
PIKECLASS Fortuna |
{ |
CVAR struct aes_ctx aes_ctx; |
CVAR struct sha256_ctx sha_ctx; |
CVAR uint8_t *key; |
CVAR uint8_t *ctr; |
CVAR uint8_t *data; |
|
DECLARE_STORAGE; |
|
#ifndef AES256_KEY_SIZE |
#define AES256_KEY_SIZE (256>>3) |
#endif |
|
void fortuna_generate() |
{ |
aes_encrypt(&THIS->aes_ctx, 16, THIS->data, THIS->ctr); |
INCREMENT(16, THIS->ctr); |
} |
|
void fortuna_rekey() |
{ |
fortuna_generate(); |
MEMCPY(THIS->key, THIS->data, 16); |
fortuna_generate(); |
MEMCPY(THIS->key+16, THIS->data, 16); |
aes_set_encrypt_key(&THIS->aes_ctx, AES256_KEY_SIZE, THIS->key); |
} |
|
/*! @decl void reseed(string(8bit) data) |
*! Generates new a new key based on the provided additional |
*! entropy. |
*/ |
PIKEFUN void reseed(string(8bit) data) |
{ |
sha256_update(&THIS->sha_ctx, 32, THIS->key); |
sha256_update(&THIS->sha_ctx, data->len, (const uint8_t *)data->str); |
sha256_digest(&THIS->sha_ctx, 32, THIS->key); |
aes_set_encrypt_key(&THIS->aes_ctx, AES256_KEY_SIZE, THIS->key); |
INCREMENT(16, THIS->ctr); |
} |
|
/*! @decl string(8bit) random_string(int(0..) len) |
*! |
*! Generates @[len] amount of pseudo random data. In contrast with |
*! the Fortuna PseudoRandomData function, which only allows 2^20 |
*! bytes of random data per call, the necessary rekey operations |
*! are here performed internally, so no such restrictions apply. |
*/ |
PIKEFUN string(8bit) random_string(int(0..) len) |
{ |
unsigned stored = 0; |
struct string_builder s; |
init_string_builder_alloc(&s, len+16, 0); |
|
while( stored < len ) |
{ |
fortuna_generate(); |
string_builder_binary_strcat(&s, (const char *)THIS->data, |
MIN(16, (len-stored))); |
stored += 16; |
if( !(stored % (1<<20)) ) |
fortuna_rekey(); |
} |
fortuna_rekey(); |
|
RETURN finish_string_builder(&s); |
} |
|
INIT |
{ |
THIS->ctr = xalloc(16); |
memset(THIS->ctr,0,16); |
THIS->key = xalloc(32); |
memset(THIS->key,0,32); |
aes_set_encrypt_key(&THIS->aes_ctx, AES256_KEY_SIZE, THIS->key); |
sha256_init(&THIS->sha_ctx); |
THIS->data = xalloc(16); |
} |
|
EXIT |
gc_trivial; |
{ |
free(THIS->ctr); |
free(THIS->key); |
free(THIS->data); |
} |
} |
|
/*! @endclass |
*/ |
|
/*! @decl string(0..127) crypt_md5(string(0..255) password, @ |
*! string(0..255) salt,@ |
*! void|string(0..255) magic) |
*! Does the crypt_md5 abrakadabra (MD5 + snakeoil). It is assumed |
*! that @[salt] does not contain "$". |
*! |
*! The @[password] memory will be cleared before released. |
*/ |
PIKEFUN string(0..127) crypt_md5(string(0..255) pw, string(0..255) salt, |
void|string(0..255) magic) |
optflags OPT_TRY_OPTIMIZE; |
{ |
char *hash; |
NO_WIDE_STRING(pw); |
NO_WIDE_STRING(salt); |
|
pw->flags |= STRING_CLEAR_ON_EXIT; |
|
if(!magic) |
{ |
hash = pike_crypt_md5(pw->len, pw->str, salt->len, salt->str, |
3, "$1$"); |
} |
else |
{ |
NO_WIDE_STRING(magic); |
hash = pike_crypt_md5(pw->len, pw->str, salt->len, salt->str, |
magic->len, magic->str); |
} |
|
push_text(hash); |
} |
|
/*! @endmodule |
*/ |
|
#endif /* HAVE_LIBNETTLE */ |
|
PIKE_MODULE_INIT |
{ |
#ifdef __NT__ |
struct program *nt_program = NULL; |
struct object *nt_object = NULL; |
#endif /* __NT__ */ |
INIT; |
#ifdef HAVE_LIBNETTLE |
hash_init(); |
cipher_init(); |
#endif /* HAVE_LIBNETTLE */ |
#ifdef __NT__ |
start_new_program(); |
nt_init(); |
nt_program = end_program(); |
add_object_constant("NT", nt_object=clone_object(nt_program,0), 0); |
free_object(nt_object); |
free_program(nt_program); |
#endif /* __NT__ */ |
#ifdef HAVE_LIBHOGWEED |
hogweed_init(); |
#endif |
} |
|
PIKE_MODULE_EXIT |
{ |
#ifdef HAVE_LIBNETTLE |
cipher_exit(); |
hash_exit(); |
#endif /* HAVE_LIBNETTLE */ |
#ifdef __NT__ |
nt_exit(); |
#endif /* __NT__ */ |
#ifdef HAVE_LIBHOGWEED |
hogweed_exit(); |
#endif |
EXIT; |
} |
|