From dacc3733305ede05a20c428b0a1f2a704873483a Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 28 Jun 2015 03:36:12 -0400 Subject: Add OCaml binding for RNG and hash functions. Add hex_encode to FFI --- src/lib/ffi/ffi.cpp | 22 +++++++ src/lib/ffi/ffi.h | 47 +++++++++++++++ src/ocaml/botan.ml | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ocaml/botan.mli | 35 ++++++++++++ src/ocaml/build.sh | 3 + 5 files changed, 268 insertions(+) create mode 100644 src/ocaml/botan.ml create mode 100644 src/ocaml/botan.mli create mode 100755 src/ocaml/build.sh diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp index 2151c33a9..4fcdb63c1 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,27 @@ uint32_t botan_version_minor() { return Botan::version_minor(); } uint32_t botan_version_patch() { return Botan::version_patch(); } uint32_t botan_version_datestamp() { return Botan::version_datestamp(); } +int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len) + { + return Botan::same_mem(x, y, len) ? 0 : 1; + } + +int botan_hex_encode(const uint8_t* in, size_t len, char* out, uint32_t flags) + { + try + { + const bool uppercase = (flags & BOTAN_FFI_HEX_LOWER_CASE) == 0; + Botan::hex_encode(out, in, len, uppercase); + return 0; + } + catch(std::exception& e) + { + log_exception(BOTAN_CURRENT_FUNCTION, e.what()); + } + + return 1; + } + int botan_rng_init(botan_rng_t* rng_out, const char* rng_type) { // Just gives unique_ptr something to delete, really diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index ca92977f4..a516529b4 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -50,12 +50,23 @@ BOTAN_DLL uint32_t botan_version_datestamp(); */ BOTAN_DLL int botan_same_mem(const uint8_t* x, const uint8_t* y, size_t len); +#define BOTAN_FFI_HEX_LOWER_CASE 1 + +BOTAN_DLL int botan_hex_encode(const uint8_t* x, size_t len, char* out, uint32_t flags); + /* * RNG */ typedef struct botan_rng_struct* botan_rng_t; +/** +* TODO: replace rng_type with simple flags? +*/ BOTAN_DLL int botan_rng_init(botan_rng_t* rng, const char* rng_type); + +/** +* TODO: better name +*/ BOTAN_DLL int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len); BOTAN_DLL int botan_rng_reseed(botan_rng_t rng, size_t bits); BOTAN_DLL int botan_rng_destroy(botan_rng_t rng); @@ -65,13 +76,49 @@ BOTAN_DLL int botan_rng_destroy(botan_rng_t rng); */ typedef struct botan_hash_struct* botan_hash_t; +/** +* Initialize a hash object: +* botan_hash_t hash +* botan_hash_init(&hash, "SHA-384", 0); +* +* Flags should be 0 in current API revision, all other uses are reserved. +* and return BOTAN_FFI_ERROR_BAD_FLAG. + +* TODO: since output_length is effectively required to use this API, +* return it from init as an output parameter +*/ BOTAN_DLL int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags); + +/** +* Writes the output length of the hash object to *output_length +*/ BOTAN_DLL int botan_hash_output_length(botan_hash_t hash, size_t* output_length); + +/** +* Send more input to the hash function. +*/ BOTAN_DLL int botan_hash_update(botan_hash_t hash, const uint8_t* in, size_t in_len); + +/** +* Finalizes the hash computation and writes the output to +* out[0:botan_hash_output_length()] then reinitializes for computing +* another digest as if botan_hash_clear had been called. +*/ BOTAN_DLL int botan_hash_final(botan_hash_t hash, uint8_t out[]); + +/** +* Reinitializes the state of the hash computation. A hash can +* be computed (with update/final) immediately. +*/ BOTAN_DLL int botan_hash_clear(botan_hash_t hash); + +/** +* Frees all resources of this object +*/ BOTAN_DLL int botan_hash_destroy(botan_hash_t hash); +BOTAN_DLL int botan_hash_name(botan_hash_t hash, char* name, size_t name_len); + /* * Message Authentication */ diff --git a/src/ocaml/botan.ml b/src/ocaml/botan.ml new file mode 100644 index 000000000..8abdd04dc --- /dev/null +++ b/src/ocaml/botan.ml @@ -0,0 +1,161 @@ +(* +* OCaml binding for botan (http://botan.randombit.net) +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*) + +open Ctypes +open Foreign + +exception Botan_Error of int + +(* TODO: translate error code to string *) +let result_or_exn rc res = + match rc with + | 0 -> res + | _ as ec -> raise (Botan_Error ec) + + +module Botan = struct + + let version = + let version_major = + foreign "botan_version_major" (void @-> returning int32_t) in + let version_minor = + foreign "botan_version_minor" (void @-> returning int32_t) in + let version_patch = + foreign "botan_version_patch" (void @-> returning int32_t) in + let major = Int32.to_int (version_major ()) in + let minor = Int32.to_int (version_minor ()) in + let patch = Int32.to_int (version_patch ()) in + (major, minor, patch) + + let version_string = + let version_string = + foreign "botan_version_string" (void @-> returning string) in + version_string () + + let version_date = + let version_datestamp = + foreign "botan_version_datestamp" (void @-> returning int32_t) in + Int32.to_int (version_datestamp ()) + + let ffi_version = + let ffi_version = + foreign "botan_ffi_api_version" (void @-> returning int32_t) in + Int32.to_int (ffi_version ()) + + let hex_encode bin = + let hex_encode = + foreign "botan_hex_encode" (string @-> size_t @-> ptr char @-> uint32_t @-> returning int) in + let bin_len = String.length bin in + let hex_len = 2*bin_len in + let hex = allocate_n char hex_len in + let rc = hex_encode bin (Unsigned.Size_t.of_int bin_len) hex (Unsigned.UInt32.of_int 0) in + result_or_exn rc (string_from_ptr hex hex_len) + + module Hash = struct + type t = unit ptr + let hash_t : t typ = ptr void + + let create name = + let hash_init = + foreign "botan_hash_init" (ptr hash_t @-> string @-> uint32_t @-> returning int) in + let o = allocate_n ~count:1 hash_t in + let rc = hash_init o name (Unsigned.UInt32.of_int 0) in + result_or_exn rc (!@ o) + + let destroy hash = + let hash_destroy = + foreign "botan_hash_destroy" (hash_t @-> returning int) in + let rc = hash_destroy hash in + result_or_exn rc () + + let output_length hash = + let hash_output_length = + foreign "botan_hash_output_length" (hash_t @-> ptr size_t @-> returning int) in + let ol = allocate_n ~count:1 size_t in + let rc = hash_output_length hash ol in + result_or_exn rc (Unsigned.Size_t.to_int (!@ ol)) + + let clear hash = + let hash_clear = + foreign "botan_hash_clear" (hash_t @-> returning int) in + let rc = hash_clear hash in + result_or_exn rc () + + let update hash input = + let hash_update = + foreign "botan_hash_update" (hash_t @-> string @-> size_t @-> returning int) in + let input_len = (String.length input) in + let rc = hash_update hash input (Unsigned.Size_t.of_int input_len) in + result_or_exn rc () + + let final hash = + let hash_final = + foreign "botan_hash_final" (hash_t @-> ptr char @-> returning int) in + let ol = output_length hash in + let res = allocate_n ~count:ol char in + let rc = hash_final hash res in + result_or_exn rc (string_from_ptr res ol) + + end (* Hash *) + + module RNG = struct + type t = unit ptr + let rng_t : t typ = ptr void + + let create name = + let rng_init = + foreign "botan_rng_init" (ptr rng_t @-> string @-> uint32_t @-> returning int) in + let o = allocate_n ~count:1 rng_t in + let rc = rng_init o name (Unsigned.UInt32.of_int 0) in + result_or_exn rc (!@ o) + + let destroy rng = + let rng_destroy = + foreign "botan_rng_destroy" (rng_t @-> returning int) in + let rc = rng_destroy rng in + result_or_exn rc () + + let generate rng out_len = + let rng_generate = + foreign "botan_rng_get" (rng_t @-> ptr char @-> size_t @-> returning int) in + let res = allocate_n ~count:out_len char in + let rc = rng_generate rng res (Unsigned.Size_t.of_int out_len) in + result_or_exn rc (string_from_ptr res out_len) + + let reseed rng bits = + let rng_reseed = + foreign "botan_rng_reseed" (rng_t @-> size_t @-> returning int) in + let rc = rng_reseed rng (Unsigned.Size_t.of_int bits) in + result_or_exn rc () + + let update rng input = + let rng_update = + foreign "botan_rng_update" (rng_t @-> string @-> size_t @-> returning int) in + let input_len = (String.length input) in + let rc = rng_update rng input (Unsigned.Size_t.of_int input_len) in + result_or_exn rc () + + end (* RNG *) + +end (* Botan *) + +let () = + let rng = Botan.RNG.create "user" in + print_string (Botan.hex_encode (Botan.RNG.generate rng 11) ^ "\n") + +let () = + let (maj,min,patch) = Botan.version in + let ver_str = Botan.version_string in + print_string (Printf.sprintf "%d.%d.%d\n%s\n" maj min patch ver_str) + +let () = + let h = Botan.Hash.create "SHA-384" in + begin + Botan.Hash.update h "hi"; + print_string (Botan.hex_encode (Botan.Hash.final h) ^ "\n"); + Botan.Hash.destroy h + end diff --git a/src/ocaml/botan.mli b/src/ocaml/botan.mli new file mode 100644 index 000000000..ec7e71dfe --- /dev/null +++ b/src/ocaml/botan.mli @@ -0,0 +1,35 @@ +(* +* OCaml binding for botan (http://botan.randombit.net) +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*) + + +module Botan : sig + val version : (int * int * int) + val version_string : string + val version_date : int + val ffi_version : int + + val hex_encode : string -> string + + module RNG : sig + type t + val create : string -> t + val destroy: t -> unit (* TODO: GC finalize instead *) + val generate : t -> int -> string + val reseed : t -> int -> unit + end + + module Hash : sig + type t + val create : string -> t + val destroy: t -> unit (* TODO: GC finalize instead *) + val output_length : t -> int + val clear : t -> unit + val update : t -> string -> unit + val final: t -> string + end + +end diff --git a/src/ocaml/build.sh b/src/ocaml/build.sh new file mode 100755 index 000000000..8b1c0825d --- /dev/null +++ b/src/ocaml/build.sh @@ -0,0 +1,3 @@ + +# extra ../ is needed due to ocamlbuild chdiring into _build +ocamlbuild -pkg ctypes.foreign -lflags -cclib,-L../../.. -lflags -cclib,-lbotan-1.11 botan.native -- cgit v1.2.3