/* 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 |
*/ |
|
/*! @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 pw, string salt, |
void|string magic) |
optflags OPT_TRY_OPTIMIZE; |
/* NB: We use a weaker type above to allow us to delay |
* throwing errors on wide strings until we've had |
* time to censor the password string. |
*/ |
rawtype tFunc(tStr8 tStr8 tOr(tStr8, tVoid), tStr7); |
{ |
char *hash; |
|
/* Censor the password. */ |
push_string(pw); |
args++; |
add_ref(Pike_sp[-args].u.string = MK_STRING("censored")); |
|
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); |
} |
|
#if 0 |
|
/* @class LFib |
* The Donald Knuth Lagged Fibonacci pseudo random number generator. |
* This is @b{not@} a source for cryptographic randomness. Use |
* @[Crypto.Yarrow] instead. |
*/ |
PIKECLASS LFib |
{ |
CVAR struct knuth_lfib_ctx *ctx; |
|
INIT { |
THIS->ctx = xalloc(sizeof(struct knuth_lfib_ctx)); |
} |
|
EXIT |
gc_trivial; |
{ |
free(THIS->ctx); |
} |
|
/* @decl void create(int seed) |
* The Lfib generator must be seeded with a number. |
*/ |
PIKEFUN void create(int seed) |
flags ID_PROTECTED; |
{ |
knuth_lfib_init(THIS->ctx, seed); |
} |
|
/* @decl this_program reseed(int s) |
* Reseed this object with seed @[s]. |
* @return |
* Returns the current object. |
*/ |
PIKEFUN object reseed(int s) { |
knuth_lfib_init(THIS->ctx, s); |
RETURN this_object(); |
} |
|
/* Get one 32bit pseudorandom integer. |
*/ |
PIKEFUN int get() { |
RETURN knuth_lfib_get(THIS->ctx); |
} |
|
/* Get a pseudorandom string of length @[len]. |
*/ |
PIKEFUN string(0..255) get_string(int len) { |
struct pike_string *s = begin_shared_string(len); |
knuth_lfib_random(THIS->ctx, len, s->str); |
push_string(end_shared_string(s)); |
} |
} |
|
/* @endclass |
*/ |
|
#endif |
|
/*! @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; |
} |
|