/* -*- 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. |
|| $Id: hash.cmod,v 1.35 2010/06/21 12:35:35 mast Exp $ |
*/ |
|
#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 <assert.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); |
|
THREADS_ALLOW(); |
meta->init(ctx); |
meta->update(ctx, in->len, (const uint8_t *)in->str); |
THREADS_DISALLOW(); |
|
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); |
THREADS_ALLOW(); |
meta->update(ctx, data->len, (const uint8_t *)data->str); |
THREADS_DISALLOW(); |
|
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 (arg->type != 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 */ |
|
/*! @class MD5_Info |
*! |
*! Internal mixin class, intended to be multiply inherited |
*! together with HashInfo. */ |
|
PIKECLASS MD5_Info |
{ |
INIT |
{ |
struct HashInfo_struct *HashInfo |
= (struct HashInfo_struct *) get_storage(Pike_fp->current_object, |
HashInfo_program); |
|
werror("MD5_Info->INIT\n"); |
|
if (HashInfo && !HashInfo->meta) |
HashInfo->meta = &nettle_md5; |
else { |
/* Can't call Pike_error here. |
* Pike_error("Can't initialize this object.\n"); */ |
werror("MD5_Info->INIT failed\n"); |
} |
} |
} |
|
/*! @endclass MD5_Info */ |
|
/*! @class MD5_State |
*! |
*! State for MD5 hashing. |
*/ |
PIKECLASS MD5_State |
{ |
INHERIT MD5_Info; |
INHERIT HashState; |
CVAR struct md5_ctx md5; |
|
INIT |
{ |
struct HashState_struct *instance |
= (struct HashState_struct *) get_storage(Pike_fp->current_object, |
HashState_program); |
werror("MD5_State->INIT\n"); |
|
assert(instance); |
|
md5_init(&THIS->md5); |
instance->ctx = &THIS->md5; |
} |
} |
/*! @endclass MD5_State */ |
|
#ifdef HAVE_NETTLE_MD4_INIT |
|
/*! @class MD4_Info |
*! |
*! Internal mixin class, intended to be multiply inherited |
*! together with HashInfo. */ |
|
PIKECLASS MD4_Info |
{ |
INIT |
{ |
struct HashInfo_struct *HashInfo |
= (struct HashInfo_struct *) get_storage(Pike_fp->current_object, |
HashInfo_program); |
|
werror("MD4_Info->INIT\n"); |
|
if (HashInfo && !HashInfo->meta) |
HashInfo->meta = &nettle_md4; |
else { |
/* Can't call Pike_error here. |
* Pike_error("Can't initialize this object.\n"); */ |
werror("MD4_Info->INIT failed\n"); |
} |
} |
} |
|
/*! @endclass MD4_Info */ |
|
/*! @class MD4_State |
*! |
*! State for MD4 hashing. |
*/ |
PIKECLASS MD4_State |
{ |
INHERIT MD4_Info; |
INHERIT HashState; |
CVAR struct md4_ctx md4; |
|
INIT |
{ |
struct HashState_struct *instance |
= (struct HashState_struct *) get_storage(Pike_fp->current_object, |
HashState_program); |
werror("MD4_State->INIT\n"); |
|
assert(instance); |
|
md4_init(&THIS->md4); |
instance->ctx = &THIS->md4; |
} |
} |
/*! @endclass MD4_State */ |
|
/*! @class MD2_Info |
*! |
*! Internal mixin class, intended to be multiply inherited |
*! together with HashInfo. */ |
|
PIKECLASS MD2_Info |
{ |
INIT |
{ |
struct HashInfo_struct *HashInfo |
= (struct HashInfo_struct *) get_storage(Pike_fp->current_object, |
HashInfo_program); |
|
werror("MD2_Info->INIT\n"); |
|
if (HashInfo && !HashInfo->meta) |
HashInfo->meta = &nettle_md2; |
else { |
/* Can't call Pike_error here. |
* Pike_error("Can't initialize this object.\n"); */ |
werror("MD2_Info->INIT failed\n"); |
} |
} |
} |
|
/*! @endclass MD2_Info */ |
|
/*! @class MD2_State |
*! |
*! State for MD2 hashing. |
*/ |
PIKECLASS MD2_State |
{ |
INHERIT MD2_Info; |
INHERIT HashState; |
CVAR struct md2_ctx md2; |
|
INIT |
{ |
struct HashState_struct *instance |
= (struct HashState_struct *) get_storage(Pike_fp->current_object, |
HashState_program); |
werror("MD2_State->INIT\n"); |
|
assert(instance); |
|
md2_init(&THIS->md2); |
instance->ctx = &THIS->md2; |
} |
} |
/*! @endclass MD2_State */ |
|
#endif /* HAVE_NETTLE_MD4_INIT */ |
|
/*! @class SHA1_Info |
*! |
*! Internal mixin class, intended to be multiply inherited |
*! together with HashInfo. */ |
|
PIKECLASS SHA1_Info |
{ |
INIT |
{ |
struct HashInfo_struct *HashInfo |
= (struct HashInfo_struct *) get_storage(Pike_fp->current_object, |
HashInfo_program); |
|
werror("SHA1_Info->INIT\n"); |
|
if (HashInfo && !HashInfo->meta) |
HashInfo->meta = &nettle_sha1; |
else { |
/* Can't call Pike_error here. |
* Pike_error("Can't initialize this object.\n"); */ |
werror("SHA1_Info->INIT failed\n"); |
} |
} |
} |
|
/*! @endclass SHA1_Info */ |
|
/*! @class SHA1_State |
*! |
*! State for SHA1 hashing. |
*/ |
|
PIKECLASS SHA1_State |
{ |
INHERIT SHA1_Info; |
INHERIT HashState; |
CVAR struct sha1_ctx sha1; |
|
INIT |
{ |
struct HashState_struct *instance |
= (struct HashState_struct *) get_storage(Pike_fp->current_object, |
HashState_program); |
werror("SHA1_State->INIT\n"); |
|
assert(instance); |
|
sha1_init(&THIS->sha1); |
instance->ctx = &THIS->sha1; |
} |
} |
/*! @endclass SHA1_State */ |
|
/*! @class SHA256_Info |
*! |
*! Internal mixin class, intended to be multiply inherited |
*! together with HashInfo. */ |
|
PIKECLASS SHA256_Info |
{ |
INIT |
{ |
struct HashInfo_struct *HashInfo |
= (struct HashInfo_struct *) get_storage(Pike_fp->current_object, |
HashInfo_program); |
|
werror("SHA256_Info->INIT\n"); |
|
if (HashInfo && !HashInfo->meta) |
HashInfo->meta = &nettle_sha256; |
else { |
/* Can't call Pike_error here. |
* Pike_error("Can't initialize this object.\n"); */ |
werror("SHA256_Info->INIT failed\n"); |
} |
} |
} |
|
/*! @endclass SHA256_Info */ |
|
/*! @class SHA256_State |
*! |
*! State for SHA256 hashing. |
*/ |
|
PIKECLASS SHA256_State |
{ |
INHERIT SHA256_Info; |
INHERIT HashState; |
CVAR struct sha256_ctx sha256; |
|
INIT |
{ |
struct HashState_struct *instance |
= (struct HashState_struct *) get_storage(Pike_fp->current_object, |
HashState_program); |
werror("SHA256_State->INIT\n"); |
|
assert(instance); |
|
sha256_init(&THIS->sha256); |
instance->ctx = &THIS->sha256; |
} |
} |
/*! @endclass SHA256_State */ |
|
/*! @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 */ |
|