Merge remote-tracking branch 'origin/topic/matthias/opaque'

* origin/topic/matthias/opaque:
  Add new unit test for opaque serialization.
  Migrate entropy testing to opaque.
  C++ify RandTest.*
  Fix a hard-to-spot bug.
  Use more descriptive error message.
  Fix the fix :-/.
  Fix initialization of hash values.
  Be clearer about delegation.
  Implement serialization of opaque types.
  Update hash BiF documentation.
  Migrate free SHA* functions to SHA*Val::digest().
  Add missing type name that caused failing tests.
  Update base scripts and unit tests.
  Simplify hash function BiFs.
  Add support for opaque hash values.
  Adapt BiF & Bro parser to handle opaque types.
  More lexer/parser work.
  Implement equivalence relation for opaque types.
  Support basic serialization of opaque.
  Add opaque type to lexer, parser, and BroType.

Closes #925

Conflicts:
	aux/broccoli
This commit is contained in:
Robin Sommer 2012-12-20 16:22:09 -08:00
commit da90976170
27 changed files with 1072 additions and 428 deletions

19
CHANGES
View file

@ -1,4 +1,23 @@
2.1-263 | 2012-12-20 16:22:09 -0800
* Bro's language now has a new set of types "opaque of X". (Matthias
Vallentin)
Opaque values can be passed around like other values but they can
only be manipulated with BiF functions, not with other operators.
Currently, the following opaque types are supported:
- opaque of md5
- opaque of sha1
- opaque of sha256
- opaquey of entropy.
They go along with the corrsponding BiF functions md5_*, sha1_*,
sha256_*, and entropy_*, respectively. Note that these functions
have changed their signatures to work with opaques types rather
than global state as it was before.
2.1-240 | 2012-12-20 15:21:07 -0800 2.1-240 | 2012-12-20 15:21:07 -0800
* Improve error for invalid use of types as values. Addresses #923. * Improve error for invalid use of types as values. Addresses #923.

19
NEWS
View file

@ -31,6 +31,22 @@ New Functionality
"return" or "break" statement will fall through to subsequent cases. "return" or "break" statement will fall through to subsequent cases.
A default case label is allowed. A default case label is allowed.
- Bro's language now has a new set of types "opaque of X". Opaque
values can be passed around like other values but they can only be
manipulated with BiF functions, not with other operators. Currently,
the following opaque types are supported:
- opaque of md5
- opaque of sha1
- opaque of sha256
- opaquey of entropy.
They go along with the corrsponding BiF functions md5_*, sha1_*,
sha256_*, and entropy_*, respectively. Note that these functions
have changed their signatures to work with opaques types rather
than global state as it was before.
Changed Functionality Changed Functionality
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -44,6 +60,9 @@ Changed Functionality
make_connection_persistent(), generate_idmef(), make_connection_persistent(), generate_idmef(),
split_complete() split_complete()
- md5_*, sha1_*, sha256_*, and entropy_* have all changed
their signatures to work with opaque types (see above).
- Removed a now unused argument from "do_split" helper function. - Removed a now unused argument from "do_split" helper function.
- "this" is no longer a reserved keyword. - "this" is no longer a reserved keyword.

View file

@ -1 +1 @@
2.1-240 2.1-263

View file

@ -22,7 +22,7 @@ export {
## Indicates if an MD5 sum is being calculated for the current ## Indicates if an MD5 sum is being calculated for the current
## request/response pair. ## request/response pair.
calculating_md5: bool &default=F; md5_handle: opaque of md5 &optional;
}; };
## Generate MD5 sums for these filetypes. ## Generate MD5 sums for these filetypes.
@ -41,13 +41,12 @@ event http_entity_data(c: connection, is_orig: bool, length: count, data: string
if ( c$http$calc_md5 || if ( c$http$calc_md5 ||
(c$http?$mime_type && generate_md5 in c$http$mime_type) ) (c$http?$mime_type && generate_md5 in c$http$mime_type) )
{ {
c$http$calculating_md5 = T; c$http$md5_handle = md5_hash_init();
md5_hash_init(c$id);
} }
} }
if ( c$http$calculating_md5 ) if ( c$http?$md5_handle )
md5_hash_update(c$id, data); md5_hash_update(c$http$md5_handle, data);
} }
## In the event of a content gap during a file transfer, detect the state for ## In the event of a content gap during a file transfer, detect the state for
@ -55,11 +54,11 @@ event http_entity_data(c: connection, is_orig: bool, length: count, data: string
## incorrect anyway. ## incorrect anyway.
event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5 event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5
{ {
if ( is_orig || ! c?$http || ! c$http$calculating_md5 ) return; if ( is_orig || ! c?$http || ! c$http?$md5_handle ) return;
set_state(c, F, is_orig); set_state(c, F, is_orig);
c$http$calculating_md5 = F; md5_hash_finish(c$http$md5_handle); # Ignore return value.
md5_hash_finish(c$id); delete c$http$md5_handle;
} }
## When the file finishes downloading, finish the hash and generate a notice. ## When the file finishes downloading, finish the hash and generate a notice.
@ -67,11 +66,11 @@ event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &
{ {
if ( is_orig || ! c?$http ) return; if ( is_orig || ! c?$http ) return;
if ( c$http$calculating_md5 ) if ( c$http?$md5_handle )
{ {
local url = build_url_http(c$http); local url = build_url_http(c$http);
c$http$calculating_md5 = F; c$http$md5 = md5_hash_finish(c$http$md5_handle);
c$http$md5 = md5_hash_finish(c$id); delete c$http$md5_handle;
NOTICE([$note=MD5, $msg=fmt("%s %s %s", c$id$orig_h, c$http$md5, url), NOTICE([$note=MD5, $msg=fmt("%s %s %s", c$id$orig_h, c$http$md5, url),
$sub=c$http$md5, $conn=c, $URL=url]); $sub=c$http$md5, $conn=c, $URL=url]);
@ -82,11 +81,12 @@ event connection_state_remove(c: connection) &priority=-5
{ {
if ( c?$http_state && if ( c?$http_state &&
c$http_state$current_response in c$http_state$pending && c$http_state$current_response in c$http_state$pending &&
c$http_state$pending[c$http_state$current_response]$calculating_md5 ) c$http_state$pending[c$http_state$current_response]?$md5_handle )
{ {
# The MD5 sum isn't going to be saved anywhere since the entire # The MD5 sum isn't going to be saved anywhere since the entire
# body wouldn't have been seen anyway and we'd just be giving an # body wouldn't have been seen anyway and we'd just be giving an
# incorrect MD5 sum. # incorrect MD5 sum.
md5_hash_finish(c$id); md5_hash_finish(c$http$md5_handle);
delete c$http$md5_handle;
} }
} }

View file

@ -36,7 +36,7 @@ export {
calc_md5: bool &default=F; calc_md5: bool &default=F;
## This boolean value indicates if an MD5 sum is being calculated ## This boolean value indicates if an MD5 sum is being calculated
## for the current file transfer. ## for the current file transfer.
calculating_md5: bool &default=F; md5_handle: opaque of md5 &optional;
## Optionally write the file to disk. Must be set prior to first ## Optionally write the file to disk. Must be set prior to first
## data chunk being seen in an event. ## data chunk being seen in an event.
@ -126,18 +126,16 @@ event mime_segment_data(c: connection, length: count, data: string) &priority=-5
if ( c$smtp$current_entity$content_len == 0 ) if ( c$smtp$current_entity$content_len == 0 )
{ {
if ( generate_md5 in c$smtp$current_entity$mime_type && ! never_calc_md5 ) local entity = c$smtp$current_entity;
c$smtp$current_entity$calc_md5 = T; if ( generate_md5 in entity$mime_type && ! never_calc_md5 )
entity$calc_md5 = T;
if ( c$smtp$current_entity$calc_md5 ) if ( entity$calc_md5 )
{ entity$md5_handle = md5_hash_init();
c$smtp$current_entity$calculating_md5 = T;
md5_hash_init(c$id);
}
} }
if ( c$smtp$current_entity$calculating_md5 ) if ( c$smtp$current_entity?$md5_handle )
md5_hash_update(c$id, data); md5_hash_update(entity$md5_handle, data);
} }
## In the event of a content gap during the MIME transfer, detect the state for ## In the event of a content gap during the MIME transfer, detect the state for
@ -147,10 +145,11 @@ event content_gap(c: connection, is_orig: bool, seq: count, length: count) &prio
{ {
if ( is_orig || ! c?$smtp || ! c$smtp?$current_entity ) return; if ( is_orig || ! c?$smtp || ! c$smtp?$current_entity ) return;
if ( c$smtp$current_entity$calculating_md5 ) local entity = c$smtp$current_entity;
if ( entity?$md5_handle )
{ {
c$smtp$current_entity$calculating_md5 = F; md5_hash_finish(entity$md5_handle);
md5_hash_finish(c$id); delete entity$md5_handle;
} }
} }
@ -161,12 +160,14 @@ event mime_end_entity(c: connection) &priority=-3
if ( ! c?$smtp || ! c$smtp?$current_entity ) if ( ! c?$smtp || ! c$smtp?$current_entity )
return; return;
if ( c$smtp$current_entity$calculating_md5 ) local entity = c$smtp$current_entity;
if ( entity?$md5_handle )
{ {
c$smtp$current_entity$md5 = md5_hash_finish(c$id); entity$md5 = md5_hash_finish(entity$md5_handle);
delete entity$md5_handle;
NOTICE([$note=MD5, $msg=fmt("Calculated a hash for a MIME entity from %s", c$id$orig_h), NOTICE([$note=MD5, $msg=fmt("Calculated a hash for a MIME entity from %s", c$id$orig_h),
$sub=c$smtp$current_entity$md5, $conn=c]); $sub=entity$md5, $conn=c]);
} }
} }

View file

@ -361,6 +361,7 @@ set(bro_SRCS
NetVar.cc NetVar.cc
NetbiosSSN.cc NetbiosSSN.cc
Obj.cc Obj.cc
OpaqueVal.cc
OSFinger.cc OSFinger.cc
PacketFilter.cc PacketFilter.cc
PacketSort.cc PacketSort.cc

501
src/OpaqueVal.cc Normal file
View file

@ -0,0 +1,501 @@
#include "OpaqueVal.h"
#include "Reporter.h"
#include "Serializer.h"
bool HashVal::IsValid() const
{
return valid;
}
bool HashVal::Init()
{
if ( valid )
return false;
valid = DoInit();
return valid;
}
StringVal* HashVal::Get()
{
if ( ! valid )
return new StringVal("");
StringVal* result = DoGet();
valid = false;
return result;
}
bool HashVal::Feed(const void* data, size_t size)
{
if ( valid )
return DoFeed(data, size);
reporter->InternalError("invalid opaque hash value");
return false;
}
bool HashVal::DoInit()
{
assert(! "missing implementation of DoInit()");
return false;
}
bool HashVal::DoFeed(const void*, size_t)
{
assert(! "missing implementation of DoFeed()");
return false;
}
StringVal* HashVal::DoGet()
{
assert(! "missing implementation of DoGet()");
return new StringVal("");
}
HashVal::HashVal(OpaqueType* t) : OpaqueVal(t)
{
valid = false;
}
IMPLEMENT_SERIAL(HashVal, SER_HASH_VAL);
bool HashVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_HASH_VAL, OpaqueVal);
return SERIALIZE(valid);
}
bool HashVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(OpaqueVal);
return UNSERIALIZE(&valid);
}
void MD5Val::digest(val_list& vlist, u_char result[MD5_DIGEST_LENGTH])
{
MD5_CTX h;
md5_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
md5_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
md5_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
md5_final(&h, result);
}
void MD5Val::hmac(val_list& vlist,
u_char key[MD5_DIGEST_LENGTH],
u_char result[MD5_DIGEST_LENGTH])
{
digest(vlist, result);
for ( int i = 0; i < MD5_DIGEST_LENGTH; ++i )
result[i] ^= key[i];
MD5(result, MD5_DIGEST_LENGTH, result);
}
bool MD5Val::DoInit()
{
assert(! IsValid());
md5_init(&ctx);
return true;
}
bool MD5Val::DoFeed(const void* data, size_t size)
{
if ( ! IsValid() )
return false;
md5_update(&ctx, data, size);
return true;
}
StringVal* MD5Val::DoGet()
{
if ( ! IsValid() )
return new StringVal("");
u_char digest[MD5_DIGEST_LENGTH];
md5_final(&ctx, digest);
return new StringVal(md5_digest_print(digest));
}
IMPLEMENT_SERIAL(MD5Val, SER_MD5_VAL);
bool MD5Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_MD5_VAL, HashVal);
if ( ! IsValid() )
return true;
if ( ! (SERIALIZE(ctx.A) &&
SERIALIZE(ctx.B) &&
SERIALIZE(ctx.C) &&
SERIALIZE(ctx.D) &&
SERIALIZE(ctx.Nl) &&
SERIALIZE(ctx.Nh)) )
return false;
for ( int i = 0; i < MD5_LBLOCK; ++i )
{
if ( ! SERIALIZE(ctx.data[i]) )
return false;
}
if ( ! SERIALIZE(ctx.num) )
return false;
return true;
}
bool MD5Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(HashVal);
if ( ! IsValid() )
return true;
if ( ! (UNSERIALIZE(&ctx.A) &&
UNSERIALIZE(&ctx.B) &&
UNSERIALIZE(&ctx.C) &&
UNSERIALIZE(&ctx.D) &&
UNSERIALIZE(&ctx.Nl) &&
UNSERIALIZE(&ctx.Nh)) )
return false;
for ( int i = 0; i < MD5_LBLOCK; ++i )
{
if ( ! UNSERIALIZE(&ctx.data[i]) )
return false;
}
if ( ! UNSERIALIZE(&ctx.num) )
return false;
return true;
}
void SHA1Val::digest(val_list& vlist, u_char result[SHA_DIGEST_LENGTH])
{
SHA_CTX h;
sha1_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha1_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha1_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha1_final(&h, result);
}
bool SHA1Val::DoInit()
{
assert(! IsValid());
sha1_init(&ctx);
return true;
}
bool SHA1Val::DoFeed(const void* data, size_t size)
{
if ( ! IsValid() )
return false;
sha1_update(&ctx, data, size);
return true;
}
StringVal* SHA1Val::DoGet()
{
if ( ! IsValid() )
return new StringVal("");
u_char digest[SHA_DIGEST_LENGTH];
sha1_final(&ctx, digest);
return new StringVal(sha1_digest_print(digest));
}
IMPLEMENT_SERIAL(SHA1Val, SER_SHA1_VAL);
bool SHA1Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SHA1_VAL, HashVal);
if ( ! IsValid() )
return true;
if ( ! (SERIALIZE(ctx.h0) &&
SERIALIZE(ctx.h1) &&
SERIALIZE(ctx.h2) &&
SERIALIZE(ctx.h3) &&
SERIALIZE(ctx.h4) &&
SERIALIZE(ctx.Nl) &&
SERIALIZE(ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! SERIALIZE(ctx.data[i]) )
return false;
}
if ( ! SERIALIZE(ctx.num) )
return false;
return true;
}
bool SHA1Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(HashVal);
if ( ! IsValid() )
return true;
if ( ! (UNSERIALIZE(&ctx.h0) &&
UNSERIALIZE(&ctx.h1) &&
UNSERIALIZE(&ctx.h2) &&
UNSERIALIZE(&ctx.h3) &&
UNSERIALIZE(&ctx.h4) &&
UNSERIALIZE(&ctx.Nl) &&
UNSERIALIZE(&ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! UNSERIALIZE(&ctx.data[i]) )
return false;
}
if ( ! UNSERIALIZE(&ctx.num) )
return false;
return true;
}
void SHA256Val::digest(val_list& vlist, u_char result[SHA256_DIGEST_LENGTH])
{
SHA256_CTX h;
sha256_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha256_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha256_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha256_final(&h, result);
}
bool SHA256Val::DoInit()
{
assert( ! IsValid() );
sha256_init(&ctx);
return true;
}
bool SHA256Val::DoFeed(const void* data, size_t size)
{
if ( ! IsValid() )
return false;
sha256_update(&ctx, data, size);
return true;
}
StringVal* SHA256Val::DoGet()
{
if ( ! IsValid() )
return new StringVal("");
u_char digest[SHA256_DIGEST_LENGTH];
sha256_final(&ctx, digest);
return new StringVal(sha256_digest_print(digest));
}
IMPLEMENT_SERIAL(SHA256Val, SER_SHA256_VAL);
bool SHA256Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SHA256_VAL, HashVal);
if ( ! IsValid() )
return true;
for ( int i = 0; i < 8; ++i )
{
if ( ! SERIALIZE(ctx.h[i]) )
return false;
}
if ( ! (SERIALIZE(ctx.Nl) &&
SERIALIZE(ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! SERIALIZE(ctx.data[i]) )
return false;
}
if ( ! (SERIALIZE(ctx.num) &&
SERIALIZE(ctx.md_len)) )
return false;
return true;
}
bool SHA256Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(HashVal);
if ( ! IsValid() )
return true;
for ( int i = 0; i < 8; ++i )
{
if ( ! UNSERIALIZE(&ctx.h[i]) )
return false;
}
if ( ! (UNSERIALIZE(&ctx.Nl) &&
UNSERIALIZE(&ctx.Nh)) )
return false;
for ( int i = 0; i < SHA_LBLOCK; ++i )
{
if ( ! UNSERIALIZE(&ctx.data[i]) )
return false;
}
if ( ! (UNSERIALIZE(&ctx.num) &&
UNSERIALIZE(&ctx.md_len)) )
return false;
return true;
}
bool EntropyVal::Feed(const void* data, size_t size)
{
state.add(data, size);
return true;
}
bool EntropyVal::Get(double *r_ent, double *r_chisq, double *r_mean,
double *r_montepicalc, double *r_scc)
{
state.end(r_ent, r_chisq, r_mean, r_montepicalc, r_scc);
return true;
}
IMPLEMENT_SERIAL(EntropyVal, SER_ENTROPY_VAL);
bool EntropyVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_ENTROPY_VAL, OpaqueVal);
for ( int i = 0; i < 256; ++i )
{
if ( ! SERIALIZE(state.ccount[i]) )
return false;
}
if ( ! (SERIALIZE(state.totalc) &&
SERIALIZE(state.mp) &&
SERIALIZE(state.sccfirst)) )
return false;
for ( int i = 0; i < RT_MONTEN; ++i )
{
if ( ! SERIALIZE(state.monte[i]) )
return false;
}
if ( ! (SERIALIZE(state.inmont) &&
SERIALIZE(state.mcount) &&
SERIALIZE(state.cexp) &&
SERIALIZE(state.montex) &&
SERIALIZE(state.montey) &&
SERIALIZE(state.montepi) &&
SERIALIZE(state.sccu0) &&
SERIALIZE(state.scclast) &&
SERIALIZE(state.scct1) &&
SERIALIZE(state.scct2) &&
SERIALIZE(state.scct3)) )
return false;
return true;
}
bool EntropyVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(OpaqueVal);
for ( int i = 0; i < 256; ++i )
{
if ( ! UNSERIALIZE(&state.ccount[i]) )
return false;
}
if ( ! (UNSERIALIZE(&state.totalc) &&
UNSERIALIZE(&state.mp) &&
UNSERIALIZE(&state.sccfirst)) )
return false;
for ( int i = 0; i < RT_MONTEN; ++i )
{
if ( ! UNSERIALIZE(&state.monte[i]) )
return false;
}
if ( ! (UNSERIALIZE(&state.inmont) &&
UNSERIALIZE(&state.mcount) &&
UNSERIALIZE(&state.cexp) &&
UNSERIALIZE(&state.montex) &&
UNSERIALIZE(&state.montey) &&
UNSERIALIZE(&state.montepi) &&
UNSERIALIZE(&state.sccu0) &&
UNSERIALIZE(&state.scclast) &&
UNSERIALIZE(&state.scct1) &&
UNSERIALIZE(&state.scct2) &&
UNSERIALIZE(&state.scct3)) )
return false;
return true;
}

110
src/OpaqueVal.h Normal file
View file

@ -0,0 +1,110 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef OPAQUEVAL_H
#define OPAQUEVAL_H
#include "RandTest.h"
#include "Val.h"
#include "digest.h"
class HashVal : public OpaqueVal {
public:
virtual bool IsValid() const;
virtual bool Init();
virtual bool Feed(const void* data, size_t size);
virtual StringVal* Get();
protected:
HashVal() { };
HashVal(OpaqueType* t);
virtual bool DoInit();
virtual bool DoFeed(const void* data, size_t size);
virtual StringVal* DoGet();
DECLARE_SERIAL(HashVal);
private:
// This flag exists because Get() can only be called once.
bool valid;
};
class MD5Val : public HashVal {
public:
static void digest(val_list& vlist, u_char result[MD5_DIGEST_LENGTH]);
static void hmac(val_list& vlist,
u_char key[MD5_DIGEST_LENGTH],
u_char result[MD5_DIGEST_LENGTH]);
MD5Val() : HashVal(new OpaqueType("md5")) { }
protected:
friend class Val;
virtual bool DoInit() /* override */;
virtual bool DoFeed(const void* data, size_t size) /* override */;
virtual StringVal* DoGet() /* override */;
DECLARE_SERIAL(MD5Val);
private:
MD5_CTX ctx;
};
class SHA1Val : public HashVal {
public:
static void digest(val_list& vlist, u_char result[SHA_DIGEST_LENGTH]);
SHA1Val() : HashVal(new OpaqueType("sha1")) { }
protected:
friend class Val;
virtual bool DoInit() /* override */;
virtual bool DoFeed(const void* data, size_t size) /* override */;
virtual StringVal* DoGet() /* override */;
DECLARE_SERIAL(SHA1Val);
private:
SHA_CTX ctx;
};
class SHA256Val : public HashVal {
public:
static void digest(val_list& vlist, u_char result[SHA256_DIGEST_LENGTH]);
SHA256Val() : HashVal(new OpaqueType("sha256")) { }
protected:
friend class Val;
virtual bool DoInit() /* override */;
virtual bool DoFeed(const void* data, size_t size) /* override */;
virtual StringVal* DoGet() /* override */;
DECLARE_SERIAL(SHA256Val);
private:
SHA256_CTX ctx;
};
class EntropyVal : public OpaqueVal {
public:
EntropyVal() : OpaqueVal(new OpaqueType("entropy")) { }
bool Feed(const void* data, size_t size);
bool Get(double *r_ent, double *r_chisq, double *r_mean,
double *r_montepicalc, double *r_scc);
protected:
friend class Val;
EntropyVal(OpaqueType* t);
DECLARE_SERIAL(EntropyVal);
private:
RandTest state;
};
#endif

View file

@ -12,7 +12,18 @@
Modified for Bro by Seth Hall - July 2010 Modified for Bro by Seth Hall - July 2010
*/ */
#include <RandTest.h> #include <math.h>
#include "RandTest.h"
#define log2of10 3.32192809488736234787
/* RT_LOG2 -- Calculate log to the base 2 */
static double rt_log2(double x)
{
return log2of10 * log10(x);
}
// RT_INCIRC = pow(pow(256.0, (double) (RT_MONTEN / 2)) - 1, 2.0);
#define RT_INCIRC 281474943156225.0
RandTest::RandTest() RandTest::RandTest()
{ {
@ -28,9 +39,9 @@ RandTest::RandTest()
} }
} }
void RandTest::add(void *buf, int bufl) void RandTest::add(const void *buf, int bufl)
{ {
unsigned char *bp = (unsigned char*)buf; const unsigned char *bp = static_cast<const unsigned char*>(buf);
int oc; int oc;
while (bufl-- > 0) while (bufl-- > 0)
@ -78,8 +89,8 @@ void RandTest::add(void *buf, int bufl)
} }
} }
void RandTest::end(double *r_ent, double *r_chisq, void RandTest::end(double* r_ent, double* r_chisq,
double *r_mean, double *r_montepicalc, double *r_scc) double* r_mean, double* r_montepicalc, double* r_scc)
{ {
int i; int i;
double ent, chisq, scc, datasum; double ent, chisq, scc, datasum;

View file

@ -1,34 +1,33 @@
#include <math.h> #ifndef RANDTEST_H
#define RANDTEST_H
#define log2of10 3.32192809488736234787 #include "util.h"
/* RT_LOG2 -- Calculate log to the base 2 */
static double rt_log2(double x)
{
return log2of10 * log10(x);
}
#define RT_MONTEN 6 /* Bytes used as Monte Carlo #define RT_MONTEN 6 /* Bytes used as Monte Carlo
co-ordinates. This should be no more co-ordinates. This should be no more
bits than the mantissa of your "double" bits than the mantissa of your "double"
floating point type. */ floating point type. */
class EntropyVal;
// RT_INCIRC = pow(pow(256.0, (double) (RT_MONTEN / 2)) - 1, 2.0);
#define RT_INCIRC 281474943156225.0
class RandTest { class RandTest {
public: public:
RandTest(); RandTest();
void add(void *buf, int bufl); void add(const void* buf, int bufl);
void end(double *r_ent, double *r_chisq, double *r_mean, void end(double* r_ent, double* r_chisq, double* r_mean,
double *r_montepicalc, double *r_scc); double* r_montepicalc, double* r_scc);
private: private:
long ccount[256]; /* Bins to count occurrences of values */ friend class EntropyVal;
long totalc; /* Total bytes counted */
int64 ccount[256]; /* Bins to count occurrences of values */
int64 totalc; /* Total bytes counted */
int mp; int mp;
int sccfirst; int sccfirst;
unsigned int monte[RT_MONTEN]; unsigned int monte[RT_MONTEN];
long inmont, mcount; int64 inmont, mcount;
double cexp, montex, montey, montepi, double cexp, montex, montey, montepi,
sccu0, scclast, scct1, scct2, scct3; sccu0, scclast, scct1, scct2, scct3;
}; };
#endif

View file

@ -98,6 +98,12 @@ SERIAL_VAL(RECORD_VAL, 10)
SERIAL_VAL(ENUM_VAL, 11) SERIAL_VAL(ENUM_VAL, 11)
SERIAL_VAL(VECTOR_VAL, 12) SERIAL_VAL(VECTOR_VAL, 12)
SERIAL_VAL(MUTABLE_VAL, 13) SERIAL_VAL(MUTABLE_VAL, 13)
SERIAL_VAL(OPAQUE_VAL, 14)
SERIAL_VAL(HASH_VAL, 15)
SERIAL_VAL(MD5_VAL, 16)
SERIAL_VAL(SHA1_VAL, 17)
SERIAL_VAL(SHA256_VAL, 18)
SERIAL_VAL(ENTROPY_VAL, 19)
#define SERIAL_EXPR(name, val) SERIAL_CONST(name, val, EXPR) #define SERIAL_EXPR(name, val) SERIAL_CONST(name, val, EXPR)
SERIAL_EXPR(EXPR, 1) SERIAL_EXPR(EXPR, 1)
@ -178,6 +184,7 @@ SERIAL_TYPE(SUBNET_TYPE, 8)
SERIAL_TYPE(FILE_TYPE, 9) SERIAL_TYPE(FILE_TYPE, 9)
SERIAL_TYPE(ENUM_TYPE, 10) SERIAL_TYPE(ENUM_TYPE, 10)
SERIAL_TYPE(VECTOR_TYPE, 11) SERIAL_TYPE(VECTOR_TYPE, 11)
SERIAL_TYPE(OPAQUE_TYPE, 12)
SERIAL_CONST2(ATTRIBUTES) SERIAL_CONST2(ATTRIBUTES)
SERIAL_CONST2(EVENT_HANDLER) SERIAL_CONST2(EVENT_HANDLER)

View file

@ -30,6 +30,7 @@ const char* type_name(TypeTag t)
"table", "union", "record", "types", "table", "union", "record", "types",
"func", "func",
"file", "file",
"opaque",
"vector", "vector",
"type", "type",
"error", "error",
@ -96,6 +97,7 @@ BroType::BroType(TypeTag t, bool arg_base_type)
case TYPE_LIST: case TYPE_LIST:
case TYPE_FUNC: case TYPE_FUNC:
case TYPE_FILE: case TYPE_FILE:
case TYPE_OPAQUE:
case TYPE_VECTOR: case TYPE_VECTOR:
case TYPE_TYPE: case TYPE_TYPE:
internal_tag = TYPE_INTERNAL_OTHER; internal_tag = TYPE_INTERNAL_OTHER;
@ -1262,6 +1264,41 @@ bool FileType::DoUnserialize(UnserialInfo* info)
return yield != 0; return yield != 0;
} }
OpaqueType::OpaqueType(const string& arg_name) : BroType(TYPE_OPAQUE)
{
name = arg_name;
}
void OpaqueType::Describe(ODesc* d) const
{
if ( d->IsReadable() )
d->AddSP("opaque of");
else
d->Add(int(Tag()));
d->Add(name.c_str());
}
IMPLEMENT_SERIAL(OpaqueType, SER_OPAQUE_TYPE);
bool OpaqueType::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_OPAQUE_TYPE, BroType);
return SERIALIZE(name);
}
bool OpaqueType::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroType);
char const* n;
if ( ! UNSERIALIZE_STR(&n, 0) )
return false;
name = n;
return true;
}
EnumType::EnumType(const string& arg_name) EnumType::EnumType(const string& arg_name)
: BroType(TYPE_ENUM) : BroType(TYPE_ENUM)
{ {
@ -1716,6 +1753,13 @@ int same_type(const BroType* t1, const BroType* t2, int is_init)
case TYPE_FILE: case TYPE_FILE:
return same_type(t1->YieldType(), t2->YieldType(), is_init); return same_type(t1->YieldType(), t2->YieldType(), is_init);
case TYPE_OPAQUE:
{
const OpaqueType* ot1 = (const OpaqueType*) t1;
const OpaqueType* ot2 = (const OpaqueType*) t2;
return ot1->Name() == ot2->Name() ? 1 : 0;
}
case TYPE_TYPE: case TYPE_TYPE:
return same_type(t1, t2, is_init); return same_type(t1, t2, is_init);
@ -1805,6 +1849,7 @@ int is_assignable(BroType* t)
case TYPE_VECTOR: case TYPE_VECTOR:
case TYPE_FILE: case TYPE_FILE:
case TYPE_OPAQUE:
case TYPE_TABLE: case TYPE_TABLE:
case TYPE_TYPE: case TYPE_TYPE:
return 1; return 1;

View file

@ -29,6 +29,7 @@ typedef enum {
TYPE_LIST, TYPE_LIST,
TYPE_FUNC, TYPE_FUNC,
TYPE_FILE, TYPE_FILE,
TYPE_OPAQUE,
TYPE_VECTOR, TYPE_VECTOR,
TYPE_TYPE, TYPE_TYPE,
TYPE_ERROR TYPE_ERROR
@ -499,6 +500,23 @@ protected:
BroType* yield; BroType* yield;
}; };
class OpaqueType : public BroType {
public:
OpaqueType(const string& name);
virtual ~OpaqueType() { };
const string& Name() const { return name; }
void Describe(ODesc* d) const;
protected:
OpaqueType() { }
DECLARE_SERIAL(OpaqueType)
string name;
};
class EnumType : public BroType { class EnumType : public BroType {
public: public:
EnumType(const string& arg_name); EnumType(const string& arg_name);

View file

@ -3114,6 +3114,27 @@ void VectorVal::ValDescribe(ODesc* d) const
d->Add("]"); d->Add("]");
} }
OpaqueVal::OpaqueVal(OpaqueType* t) : Val(t)
{
}
OpaqueVal::~OpaqueVal()
{
}
IMPLEMENT_SERIAL(OpaqueVal, SER_OPAQUE_VAL);
bool OpaqueVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_OPAQUE_VAL, Val);
return true;
}
bool OpaqueVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
Val* check_and_promote(Val* v, const BroType* t, int is_init) Val* check_and_promote(Val* v, const BroType* t, int is_init)
{ {

View file

@ -1013,6 +1013,20 @@ protected:
VectorType* vector_type; VectorType* vector_type;
}; };
// Base class for values with types that are managed completely internally,
// with no further script-level operators provided (other than bif
// functions). See OpaqueVal.h for derived classes.
class OpaqueVal : public Val {
public:
OpaqueVal(OpaqueType* t);
virtual ~OpaqueVal();
protected:
friend class Val;
OpaqueVal() { }
DECLARE_SERIAL(OpaqueVal);
};
// Checks the given value for consistency with the given type. If an // Checks the given value for consistency with the given type. If an
// exact match, returns it. If promotable, returns the promoted version, // exact match, returns it. If promotable, returns the promoted version,

View file

@ -530,82 +530,7 @@ function piped_exec%(program: string, to_write: string%): bool
%} %}
%%{ %%{
static void hash_md5_val(val_list& vlist, unsigned char digest[16]) #include "OpaqueVal.h"
{
MD5_CTX h;
md5_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
md5_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
md5_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
md5_final(&h, digest);
}
static void hmac_md5_val(val_list& vlist, unsigned char digest[16])
{
hash_md5_val(vlist, digest);
for ( int i = 0; i < 16; ++i )
digest[i] = digest[i] ^ shared_hmac_md5_key[i];
MD5(digest, 16, digest);
}
static void hash_sha1_val(val_list& vlist, unsigned char digest[20])
{
SHA_CTX h;
sha1_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha1_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha1_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha1_final(&h, digest);
}
static void hash_sha256_val(val_list& vlist, unsigned char digest[32])
{
SHA256_CTX h;
sha256_init(&h);
loop_over_list(vlist, i)
{
Val* v = vlist[i];
if ( v->Type()->Tag() == TYPE_STRING )
{
const BroString* str = v->AsString();
sha256_update(&h, str->Bytes(), str->Len());
}
else
{
ODesc d(DESC_BINARY);
v->Describe(&d);
sha256_update(&h, (const u_char *) d.Bytes(), d.Len());
}
}
sha256_final(&h, digest);
}
%%} %%}
## Computes the MD5 hash value of the provided list of arguments. ## Computes the MD5 hash value of the provided list of arguments.
@ -623,8 +548,8 @@ static void hash_sha256_val(val_list& vlist, unsigned char digest[32])
## friends. ## friends.
function md5_hash%(...%): string function md5_hash%(...%): string
%{ %{
unsigned char digest[16]; unsigned char digest[MD5_DIGEST_LENGTH];
hash_md5_val(@ARG@, digest); MD5Val::digest(@ARG@, digest);
return new StringVal(md5_digest_print(digest)); return new StringVal(md5_digest_print(digest));
%} %}
@ -643,8 +568,8 @@ function md5_hash%(...%): string
## friends. ## friends.
function sha1_hash%(...%): string function sha1_hash%(...%): string
%{ %{
unsigned char digest[20]; unsigned char digest[SHA_DIGEST_LENGTH];
hash_sha1_val(@ARG@, digest); SHA1Val::digest(@ARG@, digest);
return new StringVal(sha1_digest_print(digest)); return new StringVal(sha1_digest_print(digest));
%} %}
@ -663,8 +588,8 @@ function sha1_hash%(...%): string
## friends. ## friends.
function sha256_hash%(...%): string function sha256_hash%(...%): string
%{ %{
unsigned char digest[32]; unsigned char digest[SHA256_DIGEST_LENGTH];
hash_sha256_val(@ARG@, digest); SHA256Val::digest(@ARG@, digest);
return new StringVal(sha256_digest_print(digest)); return new StringVal(sha256_digest_print(digest));
%} %}
@ -679,288 +604,183 @@ function sha256_hash%(...%): string
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hmac%(...%): string function md5_hmac%(...%): string
%{ %{
unsigned char digest[16]; unsigned char hmac[MD5_DIGEST_LENGTH];
hmac_md5_val(@ARG@, digest); MD5Val::hmac(@ARG@, shared_hmac_md5_key, hmac);
return new StringVal(md5_digest_print(digest)); return new StringVal(md5_digest_print(hmac));
%} %}
%%{ ## Constructs an MD5 handle to enable incremental hash computation. You can
static map<BroString, MD5_CTX> md5_states; ## feed data to the returned opaque value with :bro:id:`md5_hash_update` and
static map<BroString, SHA_CTX> sha1_states; ## eventually need to call :bro:id:`md5_hash_finish` to finish the computation
static map<BroString, SHA256_CTX> sha256_states; ## and get the hash digest as result.
BroString* convert_index_to_string(Val* index)
{
ODesc d;
index->Describe(&d);
BroString* s = new BroString(1, d.TakeBytes(), d.Len());
s->SetUseFreeToDelete(1);
return s;
}
%%}
## Initializes MD5 state to enable incremental hash computation. After
## initializing the MD5 state with this function, you can feed data to
## :bro:id:`md5_hash_update` and finally need to call :bro:id:`md5_hash_finish`
## to finish the computation and get the final hash value.
## ##
## For example, when computing incremental MD5 values of transferred files in ## For example, when computing incremental MD5 values of transferred files in
## multiple concurrent HTTP connections, one would call ``md5_hash_init(c$id)`` ## multiple concurrent HTTP connections, one keeps an optional handle in the
## once before invoking ``md5_hash_update(c$id, some_more_data)`` in the ## HTTP session record. Then, one would call
## ``c$http$md5_handle = md5_hash_init()`` once before invoking
## ``md5_hash_update(c$http$md5_handle, some_more_data)`` in the
## :bro:id:`http_entity_data` event handler. When all data has arrived, a call ## :bro:id:`http_entity_data` event handler. When all data has arrived, a call
## to :bro:id:`md5_hash_finish` returns the final hash value. ## to :bro:id:`md5_hash_finish` returns the final hash value.
## ##
## index: The unique identifier to associate with this hash computation. ## Returns: The opaque handle associated with this hash computation.
## ##
## .. bro:see:: md5_hmac md5_hash md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_init%(index: any%): bool function md5_hash_init%(%): opaque of md5
%{ %{
BroString* s = convert_index_to_string(index); HashVal* digest = new MD5Val();
int status = 0; digest->Init();
return digest;
if ( md5_states.count(*s) < 1 )
{
MD5_CTX h;
md5_init(&h);
md5_states[*s] = h;
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Initializes SHA1 state to enable incremental hash computation. After ## Constructs an SHA1 handle to enable incremental hash computation. You can
## initializing the SHA1 state with this function, you can feed data to ## feed data to the returned opaque value with :bro:id:`sha1_hash_update` and
## :bro:id:`sha1_hash_update` and finally need to call ## finally need to call :bro:id:`sha1_hash_finish` to finish the computation
## :bro:id:`sha1_hash_finish` to finish the computation and get the final hash ## and get the hash digest as result.
## value.
## ##
## For example, when computing incremental SHA1 values of transferred files in ## For example, when computing incremental SHA1 values of transferred files in
## multiple concurrent HTTP connections, one would call ``sha1_hash_init(c$id)`` ## multiple concurrent HTTP connections, one keeps an optional handle in the
## once before invoking ``sha1_hash_update(c$id, some_more_data)`` in the ## HTTP session record. Then, one would call
## ``c$http$sha1_handle = sha1_hash_init()`` ## once before invoking
## ``sha1_hash_update(c$http$sha1_handle, some_more_data)`` in the
## :bro:id:`http_entity_data` event handler. When all data has arrived, a call ## :bro:id:`http_entity_data` event handler. When all data has arrived, a call
## to :bro:id:`sha1_hash_finish` returns the final hash value. ## to :bro:id:`sha1_hash_finish` returns the final hash value.
## ##
## index: The unique identifier to associate with this hash computation. ## Returns: The opaque handle associated with this hash computation.
## ##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_init%(index: any%): bool function sha1_hash_init%(%): opaque of sha1
%{ %{
BroString* s = convert_index_to_string(index); HashVal* digest = new SHA1Val();
int status = 0; digest->Init();
return digest;
if ( sha1_states.count(*s) < 1 )
{
SHA_CTX h;
sha1_init(&h);
sha1_states[*s] = h;
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Initializes SHA256 state to enable incremental hash computation. After ## Constructs an SHA256 handle to enable incremental hash computation. You can
## initializing the SHA256 state with this function, you can feed data to ## feed data to the returned opaque value with :bro:id:`sha256_hash_update` and
## :bro:id:`sha256_hash_update` and finally need to call ## finally need to call :bro:id:`sha256_hash_finish` to finish the computation
## :bro:id:`sha256_hash_finish` to finish the computation and get the final hash ## and get the hash digest as result.
## value.
## ##
## For example, when computing incremental SHA256 values of transferred files in ## For example, when computing incremental SHA256 values of transferred files in
## multiple concurrent HTTP connections, one would call ## multiple concurrent HTTP connections, one keeps an optional handle in the
## ``sha256_hash_init(c$id)`` once before invoking ## HTTP session record. Then, one would call
## ``sha256_hash_update(c$id, some_more_data)`` in the ## ``c$http$sha256_handle = sha256_hash_init()`` ## once before invoking
## ``sha256_hash_update(c$http$sha256_handle, some_more_data)`` in the
## :bro:id:`http_entity_data` event handler. When all data has arrived, a call ## :bro:id:`http_entity_data` event handler. When all data has arrived, a call
## to :bro:id:`sha256_hash_finish` returns the final hash value. ## to :bro:id:`sha256_hash_finish` returns the final hash value.
## ##
## index: The unique identifier to associate with this hash computation. ## Returns: The opaque handle associated with this hash computation.
## ##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_update sha256_hash_finish
function sha256_hash_init%(index: any%): bool function sha256_hash_init%(%): opaque of sha256
%{ %{
BroString* s = convert_index_to_string(index); HashVal* digest = new SHA256Val();
int status = 0; digest->Init();
return digest;
if ( sha256_states.count(*s) < 1 )
{
SHA256_CTX h;
sha256_init(&h);
sha256_states[*s] = h;
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Update the MD5 value associated with a given index. It is required to ## Updates the MD5 value associated with a given index. It is required to
## call :bro:id:`md5_hash_init` once before calling this ## call :bro:id:`md5_hash_init` once before calling this
## function. ## function.
## ##
## index: The unique identifier to associate with this hash computation. ## handle: The opaque handle associated with this hash computation.
## ##
## data: The data to add to the hash computation. ## data: The data to add to the hash computation.
## ##
## Returns: True on success.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_update%(index: any, data: string%): bool function md5_hash_update%(handle: opaque of md5, data: string%): bool
%{ %{
BroString* s = convert_index_to_string(index); bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
int status = 0; return new Val(rc, TYPE_BOOL);
if ( md5_states.count(*s) > 0 )
{
md5_update(&md5_states[*s], data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Update the SHA1 value associated with a given index. It is required to ## Updates the SHA1 value associated with a given index. It is required to
## call :bro:id:`sha1_hash_init` once before calling this ## call :bro:id:`sha1_hash_init` once before calling this
## function. ## function.
## ##
## index: The unique identifier to associate with this hash computation. ## handle: The opaque handle associated with this hash computation.
## ##
## data: The data to add to the hash computation. ## data: The data to add to the hash computation.
## ##
## Returns: True on success.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_update%(index: any, data: string%): bool function sha1_hash_update%(handle: opaque of sha1, data: string%): bool
%{ %{
BroString* s = convert_index_to_string(index); bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
int status = 0; return new Val(rc, TYPE_BOOL);
if ( sha1_states.count(*s) > 0 )
{
sha1_update(&sha1_states[*s], data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Update the SHA256 value associated with a given index. It is required to ## Updates the SHA256 value associated with a given index. It is required to
## call :bro:id:`sha256_hash_init` once before calling this ## call :bro:id:`sha256_hash_init` once before calling this
## function. ## function.
## ##
## index: The unique identifier to associate with this hash computation. ## handle: The opaque handle associated with this hash computation.
## ##
## data: The data to add to the hash computation. ## data: The data to add to the hash computation.
## ##
## Returns: True on success.
##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_finish
function sha256_hash_update%(index: any, data: string%): bool function sha256_hash_update%(handle: opaque of sha256, data: string%): bool
%{ %{
BroString* s = convert_index_to_string(index); bool rc = static_cast<HashVal*>(handle)->Feed(data->Bytes(), data->Len());
int status = 0; return new Val(rc, TYPE_BOOL);
if ( sha256_states.count(*s) > 0 )
{
sha256_update(&sha256_states[*s], data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Returns the final MD5 digest of an incremental hash computation. ## Returns the final MD5 digest of an incremental hash computation.
## ##
## index: The unique identifier of this hash computation. ## handle: The opaque handle associated with this hash computation.
## ##
## Returns: The hash value associated with the computation at *index*. ## Returns: The hash value associated with the computation of *handle*.
## ##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function md5_hash_finish%(index: any%): string function md5_hash_finish%(handle: opaque of md5%): string
%{ %{
BroString* s = convert_index_to_string(index); return static_cast<HashVal*>(handle)->Get();
StringVal* printable_digest;
if ( md5_states.count(*s) > 0 )
{
unsigned char digest[16];
md5_final(&md5_states[*s], digest);
md5_states.erase(*s);
printable_digest = new StringVal(md5_digest_print(digest));
}
else
printable_digest = new StringVal("");
delete s;
return printable_digest;
%} %}
## Returns the final SHA1 digest of an incremental hash computation. ## Returns the final SHA1 digest of an incremental hash computation.
## ##
## index: The unique identifier of this hash computation. ## handle: The opaque handle associated with this hash computation.
## ##
## Returns: The hash value associated with the computation at *index*. ## Returns: The hash value associated with the computation of *handle*.
## ##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update ## sha1_hash sha1_hash_init sha1_hash_update
## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish ## sha256_hash sha256_hash_init sha256_hash_update sha256_hash_finish
function sha1_hash_finish%(index: any%): string function sha1_hash_finish%(handle: opaque of sha1%): string
%{ %{
BroString* s = convert_index_to_string(index); return static_cast<HashVal*>(handle)->Get();
StringVal* printable_digest;
if ( sha1_states.count(*s) > 0 )
{
unsigned char digest[20];
sha1_final(&sha1_states[*s], digest);
sha1_states.erase(*s);
printable_digest = new StringVal(sha1_digest_print(digest));
}
else
printable_digest = new StringVal("");
delete s;
return printable_digest;
%} %}
## Returns the final SHA256 digest of an incremental hash computation. ## Returns the final SHA256 digest of an incremental hash computation.
## ##
## index: The unique identifier of this hash computation. ## handle: The opaque handle associated with this hash computation.
## ##
## Returns: The hash value associated with the computation at *index*. ## Returns: The hash value associated with the computation of *handle*.
## ##
## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish ## .. bro:see:: md5_hmac md5_hash md5_hash_init md5_hash_update md5_hash_finish
## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish ## sha1_hash sha1_hash_init sha1_hash_update sha1_hash_finish
## sha256_hash sha256_hash_init sha256_hash_update ## sha256_hash sha256_hash_init sha256_hash_update
function sha256_hash_finish%(index: any%): string function sha256_hash_finish%(handle: opaque of sha256%): string
%{ %{
BroString* s = convert_index_to_string(index); return static_cast<HashVal*>(handle)->Get();
StringVal* printable_digest;
if ( sha256_states.count(*s) > 0 )
{
unsigned char digest[32];
sha256_final(&sha256_states[*s], digest);
sha256_states.erase(*s);
printable_digest = new StringVal(sha256_digest_print(digest));
}
else
printable_digest = new StringVal("");
delete s;
return printable_digest;
%} %}
## Generates a random number. ## Generates a random number.
@ -1058,11 +878,6 @@ function identify_data%(data: string, return_mime: bool%): string
return new StringVal(descr); return new StringVal(descr);
%} %}
%%{
#include <RandTest.h>
static map<BroString, RandTest*> entropy_states;
%%}
## Performs an entropy test on the given data. ## Performs an entropy test on the given data.
## See http://www.fourmilab.ch/random. ## See http://www.fourmilab.ch/random.
## ##
@ -1107,13 +922,11 @@ function find_entropy%(data: string%): entropy_test_result
%{ %{
double montepi, scc, ent, mean, chisq; double montepi, scc, ent, mean, chisq;
montepi = scc = ent = mean = chisq = 0.0; montepi = scc = ent = mean = chisq = 0.0;
EntropyVal e;
e.Feed(data->Bytes(), data->Len());
e.Get(&ent, &chisq, &mean, &montepi, &scc);
RecordVal* ent_result = new RecordVal(entropy_test_result); RecordVal* ent_result = new RecordVal(entropy_test_result);
RandTest *rt = new RandTest();
rt->add((char*) data->Bytes(), data->Len());
rt->end(&ent, &chisq, &mean, &montepi, &scc);
delete rt;
ent_result->Assign(0, new Val(ent, TYPE_DOUBLE)); ent_result->Assign(0, new Val(ent, TYPE_DOUBLE));
ent_result->Assign(1, new Val(chisq, TYPE_DOUBLE)); ent_result->Assign(1, new Val(chisq, TYPE_DOUBLE));
ent_result->Assign(2, new Val(mean, TYPE_DOUBLE)); ent_result->Assign(2, new Val(mean, TYPE_DOUBLE));
@ -1124,85 +937,52 @@ function find_entropy%(data: string%): entropy_test_result
## Initializes data structures for incremental entropy calculation. ## Initializes data structures for incremental entropy calculation.
## ##
## index: An arbitrary unique value per distinct computation. ## Returns: An opaque handle to be used in subsequent operations.
##
## Returns: True on success.
## ##
## .. bro:see:: find_entropy entropy_test_add entropy_test_finish ## .. bro:see:: find_entropy entropy_test_add entropy_test_finish
function entropy_test_init%(index: any%): bool function entropy_test_init%(%): opaque of entropy
%{ %{
BroString* s = convert_index_to_string(index); return new EntropyVal();
int status = 0;
if ( entropy_states.count(*s) < 1 )
{
entropy_states[*s] = new RandTest();
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL);
%} %}
## Adds data to an incremental entropy calculation. Before using this function, ## Adds data to an incremental entropy calculation.
## one needs to invoke :bro:id:`entropy_test_init`. ##
## handle: The opaque handle representing the entropy calculation state.
## ##
## data: The data to add to the entropy calculation. ## data: The data to add to the entropy calculation.
## ##
## index: An arbitrary unique value that identifies a particular entropy
## computation.
##
## Returns: True on success. ## Returns: True on success.
## ##
## .. bro:see:: find_entropy entropy_test_add entropy_test_finish ## .. bro:see:: find_entropy entropy_test_add entropy_test_finish
function entropy_test_add%(index: any, data: string%): bool function entropy_test_add%(handle: opaque of entropy, data: string%): bool
%{ %{
BroString* s = convert_index_to_string(index); bool status = static_cast<EntropyVal*>(handle)->Feed(data->Bytes(),
int status = 0; data->Len());
if ( entropy_states.count(*s) > 0 )
{
entropy_states[*s]->add((char*) data->Bytes(), data->Len());
status = 1;
}
delete s;
return new Val(status, TYPE_BOOL); return new Val(status, TYPE_BOOL);
%} %}
## Finishes an incremental entropy calculation. Before using this function, ## Finishes an incremental entropy calculation. Before using this function,
## one needs to initialize the computation with :bro:id:`entropy_test_init` and ## one needs to obtain an opaque handle with :bro:id:`entropy_test_init` and
## add data to it via :bro:id:`entropy_test_add`. ## add data to it via :bro:id:`entropy_test_add`.
## ##
## index: An arbitrary unique value that identifies a particular entropy ## handle: The opaque handle representing the entropy calculation state.
## computation.
## ##
## Returns: The result of the entropy test. See :bro:id:`find_entropy` for a ## Returns: The result of the entropy test. See :bro:id:`find_entropy` for a
## description of the individual components. ## description of the individual components.
## ##
## .. bro:see:: find_entropy entropy_test_init entropy_test_add ## .. bro:see:: find_entropy entropy_test_init entropy_test_add
function entropy_test_finish%(index: any%): entropy_test_result function entropy_test_finish%(handle: opaque of entropy%): entropy_test_result
%{ %{
BroString* s = convert_index_to_string(index);
double montepi, scc, ent, mean, chisq; double montepi, scc, ent, mean, chisq;
montepi = scc = ent = mean = chisq = 0.0; montepi = scc = ent = mean = chisq = 0.0;
static_cast<EntropyVal*>(handle)->Get(&ent, &chisq, &mean, &montepi, &scc);
RecordVal* ent_result = new RecordVal(entropy_test_result); RecordVal* ent_result = new RecordVal(entropy_test_result);
if ( entropy_states.count(*s) > 0 )
{
RandTest *rt = entropy_states[*s];
rt->end(&ent, &chisq, &mean, &montepi, &scc);
entropy_states.erase(*s);
delete rt;
}
ent_result->Assign(0, new Val(ent, TYPE_DOUBLE)); ent_result->Assign(0, new Val(ent, TYPE_DOUBLE));
ent_result->Assign(1, new Val(chisq, TYPE_DOUBLE)); ent_result->Assign(1, new Val(chisq, TYPE_DOUBLE));
ent_result->Assign(2, new Val(mean, TYPE_DOUBLE)); ent_result->Assign(2, new Val(mean, TYPE_DOUBLE));
ent_result->Assign(3, new Val(montepi, TYPE_DOUBLE)); ent_result->Assign(3, new Val(montepi, TYPE_DOUBLE));
ent_result->Assign(4, new Val(scc, TYPE_DOUBLE)); ent_result->Assign(4, new Val(scc, TYPE_DOUBLE));
delete s;
return ent_result; return ent_result;
%} %}

View file

@ -74,6 +74,8 @@ HEX [0-9a-fA-F]+
"set" return check_c_mode(TOK_SET); "set" return check_c_mode(TOK_SET);
"table" return check_c_mode(TOK_TABLE); "table" return check_c_mode(TOK_TABLE);
"vector" return check_c_mode(TOK_VECTOR); "vector" return check_c_mode(TOK_VECTOR);
"of" return check_c_mode(TOK_OF);
"opaque" return check_c_mode(TOK_OPAQUE);
"module" return check_c_mode(TOK_MODULE); "module" return check_c_mode(TOK_MODULE);
"@ARG@" return TOK_ARG; "@ARG@" return TOK_ARG;

View file

@ -269,15 +269,15 @@ void print_event_c_body(FILE *fp)
%token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG %token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG
%token TOK_BOOL %token TOK_BOOL
%token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM %token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM TOK_OF
%token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_TABLE TOK_MODULE %token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_OPAQUE TOK_TABLE TOK_MODULE
%token TOK_ARGS TOK_ARG TOK_ARGC %token TOK_ARGS TOK_ARG TOK_ARGC
%token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT %token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT
%token TOK_ATOM TOK_INT TOK_C_TOKEN %token TOK_ATOM TOK_INT TOK_C_TOKEN
%left ',' ':' %left ',' ':'
%type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws %type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type
%type <val> TOK_ATOM TOK_BOOL %type <val> TOK_ATOM TOK_BOOL
%union { %union {
@ -584,7 +584,17 @@ args_1: args_1 ',' opt_ws arg opt_ws
{ /* empty */ } { /* empty */ }
; ;
arg: TOK_ID opt_ws ':' opt_ws TOK_ID // TODO: Migrate all other compound types to this rule. Once the BiF language
// can parse all regular Bro types, we can throw out the unnecessary
// boilerplate typedefs for addr_set, string_set, etc.
type:
TOK_OPAQUE opt_ws TOK_OF opt_ws TOK_ID
{ $$ = concat("opaque of ", $5); }
| TOK_ID
{ $$ = $1; }
;
arg: TOK_ID opt_ws ':' opt_ws type
{ args.push_back(new BuiltinFuncArg($1, $5)); } { args.push_back(new BuiltinFuncArg($1, $5)); }
| TOK_VAR_ARG | TOK_VAR_ARG
{ {
@ -594,7 +604,7 @@ arg: TOK_ID opt_ws ':' opt_ws TOK_ID
} }
; ;
return_type: ':' opt_ws TOK_ID opt_ws return_type: ':' opt_ws type opt_ws
{ {
BuiltinFuncArg* ret = new BuiltinFuncArg("", $3); BuiltinFuncArg* ret = new BuiltinFuncArg("", $3);
ret->PrintBro(fp_bro_init); ret->PrintBro(fp_bro_init);

View file

@ -11,7 +11,7 @@
%token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FILE TOK_FOR %token TOK_DOUBLE TOK_ELSE TOK_ENUM TOK_EVENT TOK_EXPORT TOK_FILE TOK_FOR
%token TOK_FUNCTION TOK_GLOBAL TOK_HOOK TOK_ID TOK_IF TOK_INT %token TOK_FUNCTION TOK_GLOBAL TOK_HOOK TOK_ID TOK_IF TOK_INT
%token TOK_INTERVAL TOK_LIST TOK_LOCAL TOK_MODULE %token TOK_INTERVAL TOK_LIST TOK_LOCAL TOK_MODULE
%token TOK_NEXT TOK_OF TOK_PATTERN TOK_PATTERN_TEXT %token TOK_NEXT TOK_OF TOK_OPAQUE TOK_PATTERN TOK_PATTERN_TEXT
%token TOK_PORT TOK_PRINT TOK_RECORD TOK_REDEF %token TOK_PORT TOK_PRINT TOK_RECORD TOK_REDEF
%token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET %token TOK_REMOVE_FROM TOK_RETURN TOK_SCHEDULE TOK_SET
%token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE %token TOK_STRING TOK_SUBNET TOK_SWITCH TOK_TABLE
@ -899,6 +899,12 @@ type:
$$ = new FileType(base_type(TYPE_STRING)); $$ = new FileType(base_type(TYPE_STRING));
} }
| TOK_OPAQUE TOK_OF TOK_ID
{
set_location(@1, @3);
$$ = new OpaqueType($3);
}
| resolve_id | resolve_id
{ {
if ( ! $1 || ! ($$ = $1->AsType()) ) if ( ! $1 || ! ($$ = $1->AsType()) )

View file

@ -298,6 +298,7 @@ local return TOK_LOCAL;
module return TOK_MODULE; module return TOK_MODULE;
next return TOK_NEXT; next return TOK_NEXT;
of return TOK_OF; of return TOK_OF;
opaque return TOK_OPAQUE;
pattern return TOK_PATTERN; pattern return TOK_PATTERN;
port return TOK_PORT; port return TOK_PORT;
print return TOK_PRINT; print return TOK_PRINT;

View file

@ -0,0 +1,4 @@
acbd18db4cc2f85cedef654fccc4a4d8
0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
[entropy=0.918296, chi_square=423.666667, mean=108.0, monte_carlo_pi=nan, serial_correlation=-0.5]

View file

@ -0,0 +1,4 @@
acbd18db4cc2f85cedef654fccc4a4d8
0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
[entropy=0.918296, chi_square=423.666667, mean=108.0, monte_carlo_pi=nan, serial_correlation=-0.5]

View file

@ -5,20 +5,14 @@
event bro_init() event bro_init()
{ {
local a = "dh3Hie02uh^s#Sdf9L3frd243h$d78r2G4cM6*Q05d(7rh46f!0|4-f"; local a = "dh3Hie02uh^s#Sdf9L3frd243h$d78r2G4cM6*Q05d(7rh46f!0|4-f";
if ( entropy_test_init(1) != T ) local handle = entropy_test_init();
if ( ! entropy_test_add(handle, a) )
exit(1); exit(1);
print entropy_test_finish(handle);
if ( entropy_test_add(1, a) != T )
exit(1);
print entropy_test_finish(1);
local b = "0011000aaabbbbcccc000011111000000000aaaabbbbcccc0000000"; local b = "0011000aaabbbbcccc000011111000000000aaaabbbbcccc0000000";
if ( entropy_test_init(2) != T ) handle = entropy_test_init();
if ( ! entropy_test_add(handle, b) )
exit(1); exit(1);
print entropy_test_finish(handle);
if ( entropy_test_add(2, b) != T )
exit(1);
print entropy_test_finish(2);
} }

View file

@ -4,16 +4,16 @@
print md5_hash("one"); print md5_hash("one");
print md5_hash("one", "two", "three"); print md5_hash("one", "two", "three");
md5_hash_init("a"); local a = md5_hash_init();
md5_hash_init("b"); local b = md5_hash_init();
md5_hash_update("a", "one"); md5_hash_update(a, "one");
md5_hash_update("b", "one"); md5_hash_update(b, "one");
md5_hash_update("b", "two"); md5_hash_update(b, "two");
md5_hash_update("b", "three"); md5_hash_update(b, "three");
print md5_hash_finish("a"); print md5_hash_finish(a);
print md5_hash_finish("b"); print md5_hash_finish(b);
print md5_hmac("one"); print md5_hmac("one");
print md5_hmac("one", "two", "three"); print md5_hmac("one", "two", "three");

View file

@ -4,13 +4,13 @@
print sha1_hash("one"); print sha1_hash("one");
print sha1_hash("one", "two", "three"); print sha1_hash("one", "two", "three");
sha1_hash_init("a"); local a = sha1_hash_init();
sha1_hash_init("b"); local b = sha1_hash_init();
sha1_hash_update("a", "one"); sha1_hash_update(a, "one");
sha1_hash_update("b", "one"); sha1_hash_update(b, "one");
sha1_hash_update("b", "two"); sha1_hash_update(b, "two");
sha1_hash_update("b", "three"); sha1_hash_update(b, "three");
print sha1_hash_finish("a"); print sha1_hash_finish(a);
print sha1_hash_finish("b"); print sha1_hash_finish(b);

View file

@ -4,13 +4,13 @@
print sha256_hash("one"); print sha256_hash("one");
print sha256_hash("one", "two", "three"); print sha256_hash("one", "two", "three");
sha256_hash_init("a"); local a = sha256_hash_init();
sha256_hash_init("b"); local b = sha256_hash_init();
sha256_hash_update("a", "one"); sha256_hash_update(a, "one");
sha256_hash_update("b", "one"); sha256_hash_update(b, "one");
sha256_hash_update("b", "two"); sha256_hash_update(b, "two");
sha256_hash_update("b", "three"); sha256_hash_update(b, "three");
print sha256_hash_finish("a"); print sha256_hash_finish(a);
print sha256_hash_finish("b"); print sha256_hash_finish(b);

View file

@ -0,0 +1,77 @@
#
# @TEST-EXEC: bro -r $TRACES/empty.trace write.bro
# @TEST-EXEC: bro read.bro
# @TEST-EXEC: btest-diff expected.log
# @TEST-EXEC: btest-diff output.log
# @TEST-EXEC: cmp output.log expected.log
@TEST-START-FILE read.bro
global md5_handle: opaque of md5 &persistent &synchronized;
global sha1_handle: opaque of sha1 &persistent &synchronized;
global sha256_handle: opaque of sha256 &persistent &synchronized;
global entropy_handle: opaque of entropy &persistent &synchronized;
event bro_done()
{
local out = open("output.log");
# Finish incremental operations started by a previous Bro.
if ( md5_hash_update(md5_handle, "oo") )
print out, md5_hash_finish(md5_handle);
else
print out, "md5_hash_update() failed";
if ( sha1_hash_update(sha1_handle, "oo") )
print out, sha1_hash_finish(sha1_handle);
else
print out, "sha1_hash_update() failed";
if ( sha256_hash_update(sha256_handle, "oo") )
print out, sha256_hash_finish(sha256_handle);
else
print out, "sha256_hash_update() failed";
if ( entropy_test_add(entropy_handle, "oo") )
print out, entropy_test_finish(entropy_handle);
else
print out, "entropy_test_add() failed";
}
@TEST-END-FILE
@TEST-START-FILE write.bro
global md5_handle: opaque of md5 &persistent &synchronized;
global sha1_handle: opaque of sha1 &persistent &synchronized;
global sha256_handle: opaque of sha256 &persistent &synchronized;
global entropy_handle: opaque of entropy &persistent &synchronized;
event bro_init()
{
local out = open("expected.log");
print out, md5_hash("foo");
print out, sha1_hash("foo");
print out, sha256_hash("foo");
print out, find_entropy("foo");
# Begin incremental operations. Our goal is to feed the data string "foo" to
# the computation, but split into "f" and "oo" in two instances..
md5_handle = md5_hash_init();
if ( ! md5_hash_update(md5_handle, "f") )
print out, "md5_hash_update() failed";
sha1_handle = sha1_hash_init();
if ( ! sha1_hash_update(sha1_handle, "f") )
print out, "sha1_hash_update() failed";
sha256_handle = sha256_hash_init();
if ( ! sha256_hash_update(sha256_handle, "f") )
print out, "sha256_hash_update() failed";
entropy_handle = entropy_test_init();
if ( ! entropy_test_add(entropy_handle, "f") )
print out, "entropy_test_add() failed";
}
@TEST-END-FILE