[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)