/* -*- c -*- |
|| This file is part of Pike. For copyright information see COPYRIGHT. |
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING |
|| for more information. |
*/ |
|
#include "global.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "threads.h" |
|
/* For this_object() */ |
#include "object.h" |
#include "module_support.h" |
|
#include "nettle_config.h" |
|
#ifdef HAVE_LIBNETTLE |
|
DECLARATIONS |
|
#include "nettle.h" |
|
#include <nettle/md5.h> |
#ifdef HAVE_NETTLE_MD4_INIT |
#include <nettle/md4.h> |
#include <nettle/md2.h> |
#endif |
#include <nettle/sha.h> |
#include <nettle/nettle-meta.h> |
|
#include <stdio.h> |
#include <stdarg.h> |
#include "fdlib.h" |
|
#if 0 |
static void |
werror(const char *format, ...) |
{ |
va_list args; |
|
va_start(args, format); |
vfprintf(stderr, format, args); |
va_end(args); |
} |
#else |
#define werror(x) |
#endif |
|
static struct program *Fd_ref_program = NULL; |
static struct program *Fd_program = NULL; |
|
/*! @module Nettle */ |
|
/*! @class HashInfo |
*! |
*! Represents information about a hash algorithm, such as |
*! name, digest size, and internal block size. |
*/ |
PIKECLASS HashInfo |
{ |
CVAR const struct nettle_hash *meta; |
|
/*! @decl string name(void) |
*! |
*! Returns a human readable name for the algorithm. |
*/ |
PIKEFUN string name() |
optflags OPT_TRY_OPTIMIZE; |
{ |
if (!THIS->meta) |
Pike_error("HashInfo not properly initialized.\n"); |
|
push_text(THIS->meta->name); |
} |
|
/*! @decl string digest_size(void) |
*! |
*! Returns the size of a hash digests. |
*/ |
PIKEFUN int digest_size() |
optflags OPT_TRY_OPTIMIZE; |
{ |
if (!THIS->meta) |
Pike_error("HashInfo not properly initialized.\n"); |
|
push_int(THIS->meta->digest_size); |
} |
|
/*! @decl string block_size(void) |
*! |
*! Returns the internal block size of the hash algorithm. |
*/ |
PIKEFUN int block_size() |
optflags OPT_TRY_OPTIMIZE; |
{ |
if (!THIS->meta) |
Pike_error("HashInfo not properly initialized.\n"); |
|
push_int(THIS->meta->block_size); |
} |
|
|
/*! @decl string hash(string data) |
*! |
*! Works as a (faster) shortcut for |
*! @expr{HashState()->update(data)->digest()@}, where HashState is |
*! the hash state class corresponding to this HashInfo. |
*! |
*! @seealso |
*! @[HashState()->update()] and @[HashState()->digest()]. |
*/ |
PIKEFUN string hash(string in) |
optflags OPT_TRY_OPTIMIZE; |
{ |
void *ctx; |
struct pike_string *out; |
unsigned digest_length; |
const struct nettle_hash *meta = THIS->meta; |
|
if (!meta) |
Pike_error("HashInfo not properly initialized.\n"); |
NO_WIDE_STRING(in); |
|
ctx = (void *)alloca(meta->context_size); |
if(!ctx) |
SIMPLE_OUT_OF_MEMORY_ERROR("hash", meta->context_size); |
|
/* Only thread this block for significant data size */ |
if (in->len > THREADS_ALLOW_THRESHOLD) { |
THREADS_ALLOW(); |
meta->init(ctx); |
meta->update(ctx, in->len, (const uint8_t *)in->str); |
THREADS_DISALLOW(); |
} else { |
meta->init(ctx); |
meta->update(ctx, in->len, (const uint8_t *)in->str); |
} |
|
digest_length = meta->digest_size; |
out = begin_shared_string(digest_length); |
meta->digest(ctx, digest_length, (uint8_t *)out->str); |
|
pop_n_elems(args); |
push_string(end_shared_string(out)); |
} |
|
/*! @decl string hash(Stdio.File file, void|int bytes) |
*! |
*! Works as a (faster) shortcut for |
*! @expr{HashState()->update(Stdio.read_file(file))->digest()@}, |
*! where HashState is the hash state class corresponding to this |
*! HashInfo. |
*! |
*! @param bytes |
*! The number of bytes of the file object @[file] that should be |
*! hashed. Negative numbers are ignored and the whole file is |
*! hashed. |
*! |
*! @seealso |
*! @[Stdio.File], @[HashState()->update()] and |
*! @[HashState()->digest()]. |
*/ |
PIKEFUN string hash(object in, void|int bytes) |
optflags OPT_EXTERNAL_DEPEND; |
{ |
void *ctx; |
int len, fd; |
char *read_buffer; |
PIKE_STAT_T st; |
struct pike_string *out; |
const struct nettle_hash *meta = THIS->meta; |
|
if (!meta) |
Pike_error("HashInfo not properly initialized.\n"); |
|
/* Verify that the input is a Stdio.Fd or Stdio.Fd_ref */ |
if (!Fd_program) |
{ |
push_text("files.Fd"); |
SAFE_APPLY_MASTER("resolv",1); |
Fd_program = program_from_svalue(Pike_sp-1); |
if (!Fd_program) { |
pop_stack(); |
Pike_error("Unable to resolv files.Fd.\n"); |
} |
add_ref(Fd_program); |
pop_stack( ); |
} |
|
if (!Fd_ref_program) |
{ |
push_text("files.Fd_ref"); |
SAFE_APPLY_MASTER("resolv",1); |
Fd_ref_program = program_from_svalue(Pike_sp-1); |
if (!Fd_ref_program) { |
pop_stack(); |
Pike_error("Unable to resolv files.Fd_ref.\n"); |
} |
add_ref(Fd_ref_program); |
pop_stack( ); |
} |
|
if (!get_storage(in, Fd_program) && !get_storage(in, Fd_ref_program) ) |
Pike_error("Object not Fd or Fd_ref or subclass.\n"); |
|
safe_apply(in, "query_fd", 0); |
fd = Pike_sp[-1].u.integer; |
pop_stack(); |
|
if (fd_fstat(fd, &st)<0) |
Pike_error("File not found!\n"); |
|
if (!S_ISREG(st.st_mode)) |
Pike_error("Non-regular file.\n"); |
|
ctx = (void *)alloca(meta->context_size); |
if (!ctx) |
SIMPLE_OUT_OF_MEMORY_ERROR("hash", meta->context_size); |
|
read_buffer=(char *)malloc(8192); |
if (!read_buffer) |
SIMPLE_OUT_OF_MEMORY_ERROR("hash", 8192); |
|
THREADS_ALLOW(); |
meta->init(ctx); |
if(args==2 && bytes->u.integer>-1) { |
int bytes_left = bytes->u.integer; |
int read_bytes = MINIMUM(8192, bytes_left); |
while(read_bytes>0 && (len=fd_read(fd, read_buffer, read_bytes))>0) { |
meta->update(ctx, len, (const uint8_t *)read_buffer); |
bytes_left -= read_bytes; |
read_bytes = MINIMUM(8192, bytes_left); |
} |
} |
else |
while((len=fd_read(fd, read_buffer, 8192))>0) |
meta->update(ctx, len, (const uint8_t *)read_buffer); |
|
free(read_buffer); |
|
THREADS_DISALLOW(); |
out = begin_shared_string(meta->digest_size); |
meta->digest(ctx, meta->digest_size, (uint8_t *)out->str); |
|
pop_n_elems(args); |
push_string(end_shared_string(out)); |
} |
|
INIT |
{ |
werror("HashInfo->INIT\n"); |
THIS->meta = NULL; |
} |
} |
|
/*! @endclass HashInfo */ |
|
#define GET_META(o) \ |
( ((struct HashInfo_struct *) get_storage((o), HashInfo_program)) \ |
->meta) |
|
/* The algorithm objects have to be implemented in pike. */ |
|
/*! @class HashState |
*! |
*! Base class for hashing contexts. |
*/ |
PIKECLASS HashState |
{ |
INHERIT HashInfo; |
CVAR void *ctx; |
|
/* FIXME: Create should copy state from the other object, if |
* provided. */ |
|
/*! @decl HashState update(string data) |
*! |
*! Hashes more data. |
*/ |
PIKEFUN object update(string data) |
optflags OPT_SIDE_EFFECT; |
{ |
void *ctx = THIS->ctx; |
const struct nettle_hash *meta = |
GET_META(Pike_fp->current_object); |
|
if (!ctx || !meta) |
Pike_error("HashState not properly initialized.\n"); |
|
NO_WIDE_STRING(data); |
|
/* Only thread this block for significant data size */ |
if (data->len > THREADS_ALLOW_THRESHOLD) { |
THREADS_ALLOW(); |
meta->update(ctx, data->len, (const uint8_t *)data->str); |
THREADS_DISALLOW(); |
} else { |
meta->update(ctx, data->len, (const uint8_t *)data->str); |
} |
|
push_object(this_object()); |
} |
|
/*! @decl string digest(int|void length) |
*! |
*! Generates a digests, and resets the hashing contents. |
*! |
*! @param length |
*! If the length argument is provided, the digest is truncated |
*! to the given length. |
*! |
*! @returns |
*! The digest. |
*/ |
PIKEFUN string digest(int|void arg) |
{ |
const struct nettle_hash *meta; |
struct pike_string *digest; |
unsigned length; |
|
if (! THIS->ctx) |
Pike_error("HashState not properly initialized.\n"); |
|
meta = GET_META(Pike_fp->current_object); |
assert(meta); |
|
if (!arg) |
length = meta->digest_size; |
else |
{ |
if (TYPEOF(*arg) != PIKE_T_INT) |
Pike_error("Bad argument type.\n"); |
if (arg->u.integer < 0) |
Pike_error("Invalid length, must be positive.\n"); |
if ((unsigned)arg->u.integer > meta->digest_size) |
Pike_error("Unsupported digest length.\n"); |
|
length = arg->u.integer; |
} |
|
digest = begin_shared_string(length); |
meta->digest(THIS->ctx, length, (uint8_t *)digest->str); |
push_string(end_shared_string(digest)); |
} |
|
INIT |
{ |
werror("HashState->INIT\n"); |
THIS->ctx = NULL; |
} |
|
EXIT |
gc_trivial; |
{ |
werror("HashState->EXIT\n"); |
if (THIS->ctx && Pike_fp->current_object->prog) |
{ |
const struct nettle_hash *meta = |
GET_META(Pike_fp->current_object); |
assert(meta); |
memset(THIS->ctx, 0, meta->context_size); |
} |
} |
} |
|
/*! @endclass HashState */ |
|
#cmod_define TOSTR(DEF) #DEF |
|
#cmod_define PIKE_NAME MD5 |
#cmod_define NETTLE_NAME md5 |
#cmod_include "hash.H" |
#cmod_undef PIKE_NAME |
#cmod_undef NETTLE_NAME |
|
#ifdef HAVE_NETTLE_MD4_INIT |
|
#cmod_define PIKE_NAME MD4 |
#cmod_define NETTLE_NAME md4 |
#cmod_include "hash.H" |
#cmod_undef PIKE_NAME |
#cmod_undef NETTLE_NAME |
|
#cmod_define PIKE_NAME MD2 |
#cmod_define NETTLE_NAME md2 |
#cmod_include "hash.H" |
#cmod_undef PIKE_NAME |
#cmod_undef NETTLE_NAME |
|
#endif /* HAVE_NETTLE_MD4_INIT */ |
|
#cmod_define PIKE_NAME SHA1 |
#cmod_define NETTLE_NAME sha1 |
#cmod_include "hash.H" |
#cmod_undef PIKE_NAME |
#cmod_undef NETTLE_NAME |
|
#cmod_define PIKE_NAME SHA256 |
#cmod_define NETTLE_NAME sha256 |
#cmod_include "hash.H" |
#cmod_undef PIKE_NAME |
#cmod_undef NETTLE_NAME |
|
/*! @endmodule Nettle */ |
|
void |
hash_init(void) |
{ |
werror("Nettle, hash init\n"); |
INIT; |
} |
|
void |
hash_exit(void) |
{ |
werror("Nettle, hash exit\n"); |
if (Fd_program) { |
free_program( Fd_program ); |
} |
if (Fd_ref_program) { |
free_program( Fd_ref_program ); |
} |
EXIT; |
} |
|
#endif /* HAVE_LIBNETTLE */ |
|