[Nym3-commit] r5 - in trunk: . crypto

nym3-devel@lists.noreply.org nym3-devel@lists.noreply.org
Thu, 29 Apr 2004 18:12:28 +0200


Author: dybbuk
Date: 2004-04-29 18:12:26 +0200 (Thu, 29 Apr 2004)
New Revision: 5

Added:
   trunk/crypto/
   trunk/crypto/README
   trunk/crypto/crypto.ml
   trunk/crypto/nym.ml
   trunk/crypto/sprp.ml
   trunk/crypto/test.ml
Log:
These are some of the crypto primitives and functions that I was working
on.  If I remember correctly, I was still having some pretty hairy
issues trying to get OAEP encoding to work predictably, but I will try
to take a closer look at them.  Undoubtedly it's the result of some
goofy math error on my part.



Added: trunk/crypto/README
===================================================================
--- trunk/crypto/README	2004-04-26 21:20:53 UTC (rev 4)
+++ trunk/crypto/README	2004-04-29 16:12:26 UTC (rev 5)
@@ -0,0 +1,8 @@
+$Id: README,v 1.1.1.1 2004/02/21 01:21:54 erik Exp $
+
+UnderCaml is a type III nymserver conforming to the Underhill
+specification, which can be found at:
+
+                  <http://mixminion.net/nym-spec.txt>
+
+This package requires Cryptokit, Gz, and findlib.

Added: trunk/crypto/crypto.ml
===================================================================
--- trunk/crypto/crypto.ml	2004-04-26 21:20:53 UTC (rev 4)
+++ trunk/crypto/crypto.ml	2004-04-29 16:12:26 UTC (rev 5)
@@ -0,0 +1,337 @@
+(* counter.ml -- Counter mode block cipher mode.
+ * 
+ * Copyright (C) 2004 Erik Arneson
+ *
+ * $Id: crypto.ml,v 1.2 2004/04/27 04:47:20 erik Exp $ *)
+
+open Cryptokit
+open Cipher
+open Block
+open Nat
+open Num
+
+(* Integer to "octet string primitive" *)
+let k_mask num blocksize =
+  let s = String.make blocksize '\000' 
+  and base = 256 in
+  let rec aux idx n =
+    if n <= 0 || idx < 0 then
+      s
+    else (
+      s.[idx] <- (char_of_int (n mod 256));
+      aux (pred idx) (n / 256)
+    )
+  in
+    aux (pred blocksize) num
+
+let i2osp_old num len =
+  let x = String.make len '\000'
+  and base = 255 in
+  let rec aux idx n =
+    if idx < len && n > 0 then (
+      x.[idx] <- (char_of_int (n mod base));
+      aux (succ idx) (n / base)
+    ) else
+      x
+  in
+    aux 0 num
+
+let i2osp num len =
+  let x = String.make len '\000' in
+  let rec aux idx n =
+    if idx < len && n > 0 then (
+      x.[idx] <- Char.chr (n land 255);
+      aux (succ idx) (n lsr 8)
+    ) else
+      x
+  in
+    aux 0 num 
+
+let os2ip x =
+  let xlen = String.length x in
+  let rec aux accu idx =
+    if idx >= 0 then
+      aux ((int_of_char x.[idx]) + (accu lsl 8)) (pred idx)
+    else
+      accu
+  in
+    aux 0 (pred xlen)
+
+(* Use hidden side effects -- results in much cleaner notation. *)
+let xor_string s1 s2 =
+  assert ((String.length s1) = (String.length s2));
+  let slen = String.length s1 in
+  let sout = String.copy s2 in
+  let rec aux idx =
+    if idx < slen then (
+      sout.[idx] <- Char.chr ((Char.code s1.[idx]) lxor (Char.code s2.[idx]));
+      aux (succ idx)
+    )
+    else
+      sout
+  in
+    aux 0
+
+let random_bytes len = 
+  Random.string (Random.device_rng "/dev/urandom") len
+
+(* Most of this module is stolen or otherwise adapted from Cryptokit's
+   Bn module, which doesn't provide a public interface.  hex_to_nat is
+   used by the OAEP tests to read in the large numbers from RSA's test
+   vectors. *)
+module CNum = struct
+
+  let num_digits a = num_digits_nat a 0 (length_nat a)
+  let bytes_per_digit = length_of_digit / 8
+  let wipe_nat n = set_to_zero_nat n 0 (length_nat n)
+
+  (* Stolen and adapted from Cryptokit's bytes_of_nat function *)
+  let num_bits a =
+    let ndigits = num_digits a in
+      ndigits * length_of_digit - num_leading_zero_bits_in_digit a (ndigits-1)
+
+  let bytes_of_nat ?numbits n =
+    let nbits = num_bits n in
+      begin match numbits with
+	  None -> ()
+	| Some n -> if nbits > n then raise(Error Number_too_long)
+      end;
+      let l = ((nbits + 7) / 8) in
+      let s = String.create ((nbits + 7) / 8) in
+      let tmp = create_nat 2 in
+	for i = 0 to l - 1 do
+	  let pos = i / bytes_per_digit
+	  and shift = (i mod bytes_per_digit) * 8 in
+	    blit_nat tmp 0 n pos 1;
+	    shift_right_nat tmp 0 1 tmp 1 shift;
+	    s.[l-1-i] <- Char.unsafe_chr(nth_digit_nat tmp 0)
+	done;
+	wipe_nat tmp;
+	match numbits with
+	    None -> s
+	  | Some n ->
+	      let l' = ((n + 7) / 8) in
+		if l = l' then s else String.make (l' - l) '\000' ^ s
+
+  let num_of_bin s =
+    let bin = s in
+    let binlen = String.length bin 
+    and shift = Int(256) in
+    let rec aux accu idx =
+      if idx < binlen then
+	aux ((accu */ shift) +/ Int(Char.code bin.[idx])) (succ idx)
+      else
+	accu
+    in
+      aux (Int(0)) 0
+
+end
+
+module Lioness = struct
+
+  type t = {
+    efunc: (string -> string -> string);
+    hfunc: (string -> string)
+  }
+
+  let create ef hf =
+    { efunc = ef;
+      hfunc = hf }
+
+  let sprp () =
+    { efunc = (fun k m ->
+		 let enc = aes ~mode:(OFB 1) ~iv:(String.make 16 (char_of_int 0))
+			     k Encrypt in
+		   transform_string enc m);
+      hfunc = (hash_string (Hash.sha1()))
+    }
+
+  let k1_mask = k_mask 1 20
+  let k2_mask = k_mask 2 20
+  let k3_mask = k_mask 3 20
+
+  let encrypt_part ef hf km k msg =
+    ef (String.sub (hf (km ^ k ^ km)) 0 16) msg
+
+  let hash_part hf km k msg =
+    xor_string msg (hf (km ^ k ^ km))
+
+  (* ef is an encryption function, hf is a hash function, k is the key,
+     m is the message. *)
+  let encrypt lt k m =
+    assert ((String.length k) = 20);
+    let epart = encrypt_part lt.efunc lt.hfunc
+    and hpart = hash_part lt.hfunc
+    and lm = String.sub m 0 20
+    and rm = String.sub m 20 ((String.length m) - 20) in
+    let r1 = epart k lm rm in
+    let l1 = hpart (xor_string k k1_mask) r1 lm in
+    let r2 = epart (xor_string k k2_mask) l1 r1 in
+    let l2 = hpart (xor_string k k3_mask) r2 l1 in
+      l2 ^ r2
+
+  let decrypt lt k m =
+    assert ((String.length k) = 20);
+    let epart = encrypt_part lt.efunc lt.hfunc
+    and hpart = hash_part lt.hfunc
+    and lm = String.sub m 0 20
+    and rm = String.sub m 20 ((String.length m) - 20) in
+    let l1 = hpart (xor_string k k3_mask) rm lm in
+    let r1 = epart (xor_string k k2_mask) l1 rm in
+    let l2 = hpart (xor_string k k1_mask) r1 l1 in
+    let r2 = epart k l2 r1 in
+      l2 ^ r2
+
+end
+
+module Counter = struct
+
+  type direction =
+      Encrypt
+    | Decrypt
+
+  class ctr (iv_init: int) (iv_step: int) (cipher : block_cipher) =
+    let blocksize = cipher#blocksize in
+    object(self)
+      val mutable iv = iv_init
+      val step = iv_step
+      method blocksize = blocksize
+      method transform src src_off dst dst_off =
+	let mask = String.create blocksize 
+	and tmp_iv = k_mask iv blocksize
+	and size = min blocksize ((String.length dst) - dst_off) in
+	  cipher#transform tmp_iv 0 mask 0;
+	  String.blit mask 0 dst dst_off size;
+	  iv <- iv + step;
+	  wipe_string tmp_iv;
+	  wipe_string mask;
+	  Cryptokit.xor_string src src_off dst dst_off size
+      method wipe =
+	cipher#wipe;
+	iv <- 0
+    end
+
+  (* Wrap a CTR block cipher as a transform for Cryptokit.  We can't use
+     the standard transform, because CTR has a strange attribute -- input
+     data does /not/ need to be a multiple of blocksize.  Strange, huh? 
+
+     ** NOT FINISHED **)
+  class cipher (cipher: ctr) =
+    let blocksize = cipher#blocksize in
+    object(self)
+      val ibuf = String.create blocksize
+      val mutable used = 0
+			   
+    end
+
+  let make_ctr_transform ?(iv = 0) ?(step = 1) key dir =
+    new cipher 
+      (new ctr iv step
+	 (match dir with
+	      Encrypt -> new Block.aes_encrypt key
+	    | Decrypt -> new Block.aes_decrypt key))
+
+(* According to RFC3686, AES-CTR looks like this: 
+ *
+ *      CTRBLK := NONCE || IV || ONE 
+ *      FOR i := 1 to n-1 DO 
+ *        CT[i] := PT[i] XOR AES(CTRBLK) 
+ *        CTRBLK := CTRBLK + 1 
+ *      END 
+ *      CT[n] := PT[n] XOR TRUNC(AES(CTRBLK)) 
+ *)        
+
+end
+
+(* This module defines a PKCS#1 version 1.5 compliant RSA
+   implementation with OAEP encoding.  It utilizes the RSA module from
+   Cryptokit when possible, and is designed to pass RSA Security's PKCS#1
+   test vectors. *)
+module RSA_OAEP = struct
+  type error =
+      Message_too_long
+    | Parameter_string_too_long
+    | Decoding_error
+
+  exception Error of error
+
+  (* hlen is always 20 octet bytes, because we use SHA1 *)
+  let hlen = 20
+
+  let hex s = transform_string (Hexa.decode()) s
+  let unhex s = transform_string (Hexa.encode()) s
+
+  let overhead = (hlen * 2) + 2
+
+  let ceil_div i1 i2 =
+    int_of_float (ceil ((float_of_int i1) /. (float_of_int i2)))
+
+  let sha1_string s =
+    hash_string (Hash.sha1()) s
+
+  let mgf1 mgf_seed mask_len =
+    let tcount = ceil_div mask_len hlen in
+    let hf = sha1_string
+    and t  = String.make (tcount * hlen) '\000' in
+    let rec aux counter =
+      if counter < tcount && (counter * hlen) < mask_len then (
+	String.blit (hf (mgf_seed ^ (i2osp counter 4))) 0
+	  t (counter * hlen) hlen;
+	aux (counter + 1))
+      else
+	t
+    in
+      String.sub (aux 0) 0 mask_len
+
+  (* hlen is always 20 here *)
+  let mgf = mgf1
+
+  (* Encoded message is Y || maskedSeed || maskedDB, where Y is a 0x00 byte. *)
+  let encode label seed message keysize =
+    let mlen = String.length message in
+      assert (mlen <= (keysize - (2 * hlen) - 2));
+      let lhash = sha1_string label
+      and ps = String.make (keysize - mlen - (2 * hlen) - 2) '\000' in
+      let db = lhash ^ ps ^ (String.make 1 '\001') ^ message in
+	(* and seed = random_bytes hlen in *)
+      let db_mask = mgf seed (keysize - hlen - 1) in
+      let masked_db = xor_string db db_mask in
+      let seed_mask = mgf masked_db hlen in
+	Printf.printf "# seed = %s\n# mdb  = %s\n# mask = %s\n"
+	  (unhex seed) (unhex masked_db) (unhex db_mask);
+	(String.make 1 '\000') ^ (xor_string seed seed_mask) ^ masked_db
+
+  let decode label emessage =
+    let lhash = sha1_string label
+    and emlen = String.length emessage in
+      Printf.printf "# hlen = %d  emlen = %d\n" hlen emlen;
+    let yoct = String.sub emessage 0 1
+    and masked_seed = String.sub emessage 1 hlen
+    and masked_db = String.sub emessage (hlen + 1) 
+		      (emlen - hlen - 1) in
+    let seed_mask = mgf masked_db hlen in
+    let seed = xor_string masked_seed seed_mask in
+    let db_mask = mgf seed (emlen - hlen - 1) in
+    let db = xor_string masked_db db_mask in
+      try
+	let p_end = String.index_from db hlen '\001' in
+	  String.sub db (p_end + 1) ((String.length db) - p_end - 1)
+      with 
+	  Not_found ->
+	    Printf.printf "# lhash = %s\n#< seed = %s\n#< mdb  = %s\n#< mask = %s\n"
+	    (unhex lhash) (unhex seed) (unhex masked_db) (unhex db_mask);
+	    failwith "Bad encoding"
+	| Invalid_argument(s) ->
+	    Printf.printf "# Fatal error.  data at %d / %d\n#< db = %s\n"
+	      (String.index_from db hlen '\001') 
+	      ((String.length db) - (String.index_from db hlen '\001'))
+	      (unhex db);
+	    failwith "Stupid doohickey."
+
+  let encrypt ?(p = "") ?(seed = (random_bytes hlen)) key message =
+    RSA.encrypt key (encode p seed message (key.RSA.size / 8))
+
+  let decrypt ?(p = "") key message =
+    decode p (RSA.decrypt key message)
+
+end      

Added: trunk/crypto/nym.ml
===================================================================
--- trunk/crypto/nym.ml	2004-04-26 21:20:53 UTC (rev 4)
+++ trunk/crypto/nym.ml	2004-04-29 16:12:26 UTC (rev 5)
@@ -0,0 +1,38 @@
+(* nym.ml -- Underhill implementation!
+ *
+ * Note:  This doesn't work yet.  Doesn't even compile.  So there.
+ *
+ * $Id: nym.ml,v 1.1.1.1 2004/02/21 01:21:54 erik Exp $
+ *)
+
+open Cryptokit
+
+let z len =
+  String.make len (char_of_int 0)
+;;
+
+let rng =
+  Cryptokit.Random.device_rng "/dev/urandom"
+;;
+
+let r =
+  Cryptokit.Random.string rng
+;;
+
+(* PROCEDURE: Encrypt an octet sequence to a nymholder.
+   INPUTS:
+           pk -- The nymholder's encryption public key
+           m -- The octet sequence to encrypt
+           p -- The padding granularity -- either 1024 or 128.
+ *)
+let message_encrypt pk m p = 
+  let m_c = compress m in
+  let padding_len = (ceil ((length m_c) / p)) - (length m_c) in
+  let m_p = m ^ (z padding_len) ] in
+  let k = (r 16) in
+  let m_enc = sprp_encrypt k "" m_p in
+  let rsa_len = (length pk) - pk_overhead_len - 16 in
+  let rsa_part = pk_encrypt pk (buf_or k (substring m_enc 0 rsa_len)) in
+    buf_or rsa_part (substring m_enc rsa_len ((length m_enc) - rsa_len))
+;;
+

Added: trunk/crypto/sprp.ml
===================================================================
--- trunk/crypto/sprp.ml	2004-04-26 21:20:53 UTC (rev 4)
+++ trunk/crypto/sprp.ml	2004-04-29 16:12:26 UTC (rev 5)
@@ -0,0 +1,57 @@
+(* sprp.ml - Super-pseudorandom permutation
+ *
+ * This is an implementation of the cryptographic functions defined in the
+ * Mixminion draft, which can be found here:
+ *       <http://mixminion.net/minion-spec.txt>
+ *
+ * Note that we will use Cryptokit for our cryptographic primitives.
+ *
+ * $Id: sprp.ml,v 1.2 2004/04/27 04:47:20 erik Exp $
+ *)
+
+open Cryptokit
+
+let hash_len = 20
+let key_len = 16
+let pk_overhead_len = 42
+let pk_enc_len = 256
+let pk_max_data_len = 214
+let sprp_key_len = 20
+let header_len = 2048
+
+let k1_mask = (String.make 19 (char_of_int 0)) ^ (String.make 1 (char_of_int 1)) ;;
+let k2_mask = (String.make 19 (char_of_int 0)) ^ (String.make 1 (char_of_int 2)) ;;
+let k3_mask = (String.make 19 (char_of_int 0)) ^ (String.make 1 (char_of_int 3)) ;;
+
+let xor_string s1 s2 =
+  assert ((String.length s1) = (String.length s2));
+  let slen = String.length s1 in
+  let sout = String.copy s2 in
+    Cryptokit.xor_string s1 0 slen sout 0 slen;
+    sout
+;;
+
+(* ef is an encryption function, hf is a hash function, k is the key,
+   m is the message. *)
+let lioness_encrypt ef hf k m =
+  let k1 = String.copy k
+  and k2 = xor_string k k1_mask
+  and k3 = xor_string k k2_mask
+  and k4 = xor_string k k3_mask
+  and lm = String.sub m 0 20
+  and rm = String.sub m 20 ((String.length m) - 20) in
+  let rf = ef (String.sub 
+		 (hf 
+		    (k3 ^ 
+		     (xor_string lm 
+			(hf 
+			   (k2 ^ 
+			    (ef (String.sub (hf (k1 ^ lm ^ k1)) 0 16)
+			       rm) ^ 
+			    k2))) 
+		     ^ k3)) 
+		 0 16) re in
+  let lf = xor_string lh (hf (k4 ^ rf ^ k4)) in
+    lf ^ rf
+;;
+

Added: trunk/crypto/test.ml
===================================================================
--- trunk/crypto/test.ml	2004-04-26 21:20:53 UTC (rev 4)
+++ trunk/crypto/test.ml	2004-04-29 16:12:26 UTC (rev 5)
@@ -0,0 +1,216 @@
+(* test.ml -- RSA-OAEP PKCS#1 compliance testing for crypto.ml
+ * 
+ * Copyright (C) Erik Arneson 2004
+ *
+ * $Id: test.ml,v 1.1 2004/04/27 04:49:09 erik Exp $ *)
+
+open Cryptokit
+open Num
+open Crypto
+open RSA_OAEP
+open CNum
+
+type 'a some =
+    Some of 'a
+  | None
+
+type temp_key = {
+  mutable size: int;
+  mutable n: string;
+  mutable e: string;
+  mutable d: string;
+  mutable p: string;
+  mutable q: string;
+  mutable dp: string;
+  mutable dq: string;
+  mutable qinv: string;
+  mutable seed: string;
+  mutable message: string;
+  mutable cipher: string;
+}
+
+(* We use Cryptokit's hex transformations to handle reading in the
+   encoded data in RSA Security's test vectors. *)
+
+let reader is =
+  try
+    Some (input_line is)
+  with _ -> None
+
+let wipe_key key size =
+  key.size <- size;
+  key.n <- "";
+  key.e <- "";
+  key.d <- "";
+  key.p <- "";
+  key.q <- "";
+  key.dp <- "";
+  key.dq <- "";
+  key.qinv <- "";
+  key.seed <- "";
+  key.message <- "";
+  key.cipher <- "";
+  ()
+
+let d_mod d x =
+  let one = Int(1) in
+    mod_num d (x -/ one)
+
+let temp_to_rsa key =
+  (* Sanity check our RSA key *)
+  assert ((String.length key.n) = key.size / 8);
+  assert ((String.length key.d) = key.size / 8);
+  assert ((num_of_bin key.p) */ (num_of_bin key.q) =/ (num_of_bin key.n));
+  assert ((num_of_bin key.dp) =/ d_mod (num_of_bin key.d) (num_of_bin key.p));
+  assert ((num_of_bin key.dq) =/ d_mod (num_of_bin key.d) (num_of_bin key.q));
+  Printf.printf "# Key passed sanity checks\n" ;
+  { RSA.size = key.size;
+    RSA.n = key.n;
+    RSA.e = key.e;
+    RSA.d = key.d;
+    RSA.p = key.p;
+    RSA.q = key.q;
+    RSA.dp = key.dp;
+    RSA.dq = key.dq;
+    RSA.qinv = key.qinv }
+
+let hex s = transform_string (Hexa.decode()) s
+let unhex s = transform_string (Hexa.encode()) s
+
+let run_tests key =
+  try
+    let rsa_key = temp_to_rsa key in
+      Printf.printf "# %d bits n: %d, d: %d\n"
+	rsa_key.RSA.size (String.length rsa_key.RSA.n)
+	(String.length rsa_key.RSA.d);
+      Printf.printf "# CT length: %d  PT length: %d\n"
+	(String.length key.cipher) (String.length key.message);
+      let ct = encrypt ~seed:key.seed rsa_key key.message in
+	Printf.printf "# Ciphertext check: %s (lc: %d  lt: %d)\n"
+	  (if ct = key.cipher then "OK" else "NOT OK")
+	  (String.length key.cipher) (String.length ct);
+	let pt1 = decrypt rsa_key ct in
+	  Printf.printf "# Decryption check: %s (lc: %d  lt: %d)\n"
+	    (if pt1 = key.message then "OK" else "NOT OK")
+	    (String.length key.message) (String.length pt1);
+	  let pt2 = decrypt rsa_key key.cipher in
+	    Printf.printf "# Decoding check: %s (lc: %d  lt: %d)\n"
+	      (if pt2 = pt1 then "OK" else "NOT OK")
+	      (String.length pt2) (String.length pt1);
+  with 
+      Failure(s) ->
+	( print_string "# Test failed with exception: ";
+	  print_endline s;
+	  failwith "Bad encoding")
+    | Assert_failure(f,l,c) ->
+	Printf.printf "# Assertion failed on %s:%d char %d\n" f l c;
+	failwith "Faulty key?"
+
+(* This parameterized module allows us to do some nice polymorphic
+   things later on. *)
+module Test 
+  (Vector: sig
+     val read_number: in_channel -> string
+     val decipher_line: string -> in_channel -> temp_key -> unit
+   end) = struct
+    let rec process_file accu is =
+      match reader is with
+	  Some(line) ->
+	    Vector.decipher_line line is accu;
+	    process_file accu is
+	| None -> 
+	    run_tests accu
+  end
+
+(* Run tests against RSA's OAEP test vectors *)
+module RSA_test = struct
+  let read_number is =
+    let rec aux accu =
+      match reader is with
+	  Some(str) ->
+	    if (String.length str) = 0 then
+	      hex accu
+	    else
+	      aux (accu ^ str)
+	| None ->
+	    ""
+    in
+      aux ""
+	
+  let nk_regx = Str.regexp 
+		  "^# Example +\\([0-9]+\\): A \\([0-9]+\\)-bit RSA Key Pair"
+  let ex_regx = Str.regexp 
+		  "^# RSAES-OAEP Encryption Example \\(.*\\)"
+
+  let decipher_line line is key =
+    if (Str.string_match nk_regx line 0) then (
+      if (key.size > 0) then
+	run_tests key
+      else ();
+      wipe_key key (int_of_string (Str.matched_group 2 line));
+      Printf.printf "\n# Processing test %s (keysize %d)\n" 
+	(Str.matched_group 1 line) key.size)
+    else if (Str.string_match ex_regx line 0) then (
+      if (String.length key.cipher) > 0 then
+	run_tests key
+      else ();
+      Printf.printf "# Tests for example %s\n" (Str.matched_group 1 line);
+    )
+    else
+      match line with
+	  "# CRT coefficient qInv: " ->
+	    key.qinv <- read_number is;
+	| "# Encryption:" ->
+	    key.cipher <- read_number is
+	| "# Message to be encrypted:" ->
+	    key.message <- read_number is
+	| "# Prime p: " ->
+	    key.p <- read_number is
+	| "# Prime q: " ->
+	    key.q <- read_number is
+	| "# RSA modulus n:" ->
+	    key.n <- read_number is
+	| "# RSA private exponent d: " ->
+	    key.d <- read_number is
+	| "# RSA public exponent e: " ->
+	    key.e <- read_number is
+	| "# Seed:" -> 
+	    key.seed <- read_number is
+	| "# p's CRT exponent dP: " ->
+	    key.dp <- read_number is
+	| "# q's CRT exponent dQ: " ->
+	    key.dq <- read_number is
+	| _ -> ()
+
+end
+
+(* Run tests against Mixminion test vectors *)
+module Minion_test = struct
+
+  let read_number istream = ""
+
+  let decipher_line line istream key = ()
+
+end
+
+(* The first line of our file decides the parsing routines we use for
+   the test vector. *)
+let minion_sig = "======================================== RSA"
+let rsa_sig = "========================="
+
+let _ =
+  let file = Sys.argv.(1) in
+  let module Vect = Test(RSA_test) in
+    Vect.process_file 
+      { size = 0;
+	n = "";
+	e = "";
+	d = "";
+	p = "";
+	q = "";
+	dp = "";
+	dq = "";
+	qinv = "";
+	seed = "";
+	message = "";
+	cipher = "" } (open_in file)