diff --git a/lib/encoding.ml b/lib/encoding.ml new file mode 100644 index 0000000..20d2498 --- /dev/null +++ b/lib/encoding.ml @@ -0,0 +1,10 @@ +module Base64 = Nocrypto.Base64 +module Option = Core.Option + +let encode payload = + Cstruct.to_string @@ Base64.encode @@ Cstruct.of_string payload + + +let decode blob = + let open Option in + blob |> Cstruct.of_string |> Base64.decode >>| Cstruct.to_string diff --git a/lib/encoding.mli b/lib/encoding.mli new file mode 100644 index 0000000..7fa7229 --- /dev/null +++ b/lib/encoding.mli @@ -0,0 +1,3 @@ +val encode : string -> string + +val decode : string -> string option diff --git a/lib/encryption.ml b/lib/encryption.ml index 6533979..6c9014d 100644 --- a/lib/encryption.ml +++ b/lib/encryption.ml @@ -22,7 +22,8 @@ let encrypt msg ~pass = let proof = Hash.mine pass ~difficulty:5 in let key = of_secret proof in let iv = __compute_iv proof in - let result = encrypt ~iv ~key (Utils.pad ~basis:16 msg) in + let blob = Utils.pad ~basis:16 msg in + let result = encrypt ~iv ~key blob in result |> Base64.encode |> Cstruct.to_string @@ -32,6 +33,4 @@ let decrypt cipher ~pass = let iv = __compute_iv proof in let result = cipher |> Cstruct.of_string |> Base64.decode in let open Option in - result - >>= fun msg -> - msg |> decrypt ~iv ~key |> Cstruct.to_string |> Utils.unpad |> some + result >>= fun msg -> msg |> decrypt ~iv ~key |> Utils.unpad |> some diff --git a/lib/keys.ml b/lib/keys.ml index 8d87051..c94b556 100644 --- a/lib/keys.ml +++ b/lib/keys.ml @@ -3,6 +3,7 @@ module String = Core.String module Float = Core.Float module Int64 = Core.Int64 module Option = Core.Option +module Base64 = Nocrypto.Base64 module Defer = Utils.Defer let generate () = Random.generate512 () @@ -14,26 +15,51 @@ let genpub priv = List.map pieces ~f:__force_digest -let derive priv = Utils.bytes_of_string @@ Serialization.digest @@ genpub priv +let derive priv = Utils.bytes_of_hex @@ Serialization.digest @@ genpub priv -let export ~priv ~pass = Encryption.encrypt ~pass @@ Utils.bytes_to_string priv +let export ~priv ~pass = + let priv' = Bytes.to_string priv in + let payload = Encoding.encode priv' in + Encryption.encrypt ~pass payload -let validate_key plain = - if String.length plain == Utils._HASH_LENGTH then Some plain else None + +let validate_key blob = + try + let plain = + blob + |> Cstruct.of_string + |> Base64.decode + |> (fun opt -> Option.value_exn opt) + |> Cstruct.to_string + in + let parts = + String.split ~on:'\n' plain + |> List.map ~f:Cstruct.of_string + |> List.map ~f:Base64.decode + |> List.map ~f:(fun opt -> Option.value_exn opt) + |> List.map ~f:Cstruct.to_bytes + |> List.map ~f:Utils.bytes_to_hex + in + assert (Utils._HASH_LENGTH == String.length @@ List.nth_exn parts 0) ; + assert (Utils._HASH_LENGTH == String.length @@ List.nth_exn parts 1) ; + Some plain + with + | _ -> + None let import ~cipher ~pass = let open Option in - Encryption.decrypt ~pass cipher >>= validate_key >>| Utils.bytes_of_string + Encryption.decrypt ~pass cipher >>= validate_key >>| Bytes.of_string -let address pub = Utils.to_hex @@ Utils.bytes_to_string pub +let address pub = Utils.with_hex_prefix @@ Utils.bytes_to_hex pub let sign = Signing.sign let verify = Verification.verify -let show = Utils.bytes_to_string +let show = Utils.bytes_to_hex let load dump = - if Utils.is_hash dump then Some (Utils.bytes_of_string dump) else None + if Utils.is_hash dump then Some (Utils.bytes_of_hex dump) else None diff --git a/lib/main.ml b/lib/main.ml index 3844223..5fa7c22 100644 --- a/lib/main.ml +++ b/lib/main.ml @@ -1,10 +1,13 @@ module Option = Core.Option +module String = Core.String +module List = Core.List +module Bytes = Core.Bytes type priv = bytes type pub = bytes -let derive = Keys.derive +let __derive = Keys.derive let import = Keys.import @@ -22,36 +25,61 @@ let verify = Keys.verify (*** wrappers *****************************************************************) -let __check priv = - let pub = derive priv in - let id = "0x" ^ Utils.bytes_to_string pub in +let __decode blob : string = + let payload : string option = Encoding.decode blob in + Option.value_exn payload + + +let __split priv = + let parts = String.split ~on:'\n' @@ Bytes.to_string priv in + List.map ~f:__decode parts + + +let derive priv = Bytes.of_string @@ List.nth_exn (__split priv) 1 + +let __check_tail pub = + let id = "0x" ^ Utils.bytes_to_hex pub in if Blacklist.exists id then None else Some pub -let sign ~priv ~msg = +let __check priv = __check_tail @@ __derive priv + +let check priv = __check_tail @@ derive priv + +let sign ~priv:priv' ~msg = + let priv = Bytes.of_string @@ List.nth_exn (__split priv') 0 in let success pub = let signature = sign ~priv ~msg in - Blacklist.add @@ "0x" ^ Utils.bytes_to_string pub ; + Blacklist.add @@ "0x" ^ Utils.bytes_to_hex pub ; Some signature in let open Option in - __check priv >>= success + check priv' >>= success let import ~cipher ~pass = let open Option in import ~cipher ~pass - >>= function priv -> __check priv >>= (function _ -> Some priv) + >>= function priv -> check priv >>= (function _ -> Some priv) -let rec generate () = +let rec __generate () = let priv = Keys.generate () in let const _ _ = priv in let option = __check priv in - let step = Option.value_map option ~default:generate ~f:const in + let step = Option.value_map option ~default:__generate ~f:const in step () +(* precomputed public key together with private key *) +let generate () = + let priv = __generate () in + let pub = __derive priv in + let priv' = Encoding.encode @@ Bytes.to_string priv in + let pub' = Encoding.encode @@ Bytes.to_string pub in + Bytes.of_string (priv' ^ "\n" ^ pub') + + let pair () = let priv = generate () in let pub = derive priv in diff --git a/lib/serialization.ml b/lib/serialization.ml index 92e145a..84bb081 100644 --- a/lib/serialization.ml +++ b/lib/serialization.ml @@ -4,7 +4,7 @@ module Option = Core.Option let show pub = pub - |> List.map ~f:Utils.bytes_to_string + |> List.map ~f:Utils.bytes_to_hex |> List.reduce_exn ~f:Utils.concat_hashes @@ -12,7 +12,7 @@ let load text = let list = String.split text ~on:':' in let open Option in Utils.validate_key list - >>= fun list -> some @@ List.map ~f:Utils.bytes_of_string list + >>= fun list -> some @@ List.map ~f:Utils.bytes_of_hex list let digest pub = pub |> show |> Hash.digest diff --git a/lib/signing.ml b/lib/signing.ml index 8dd5898..214e944 100644 --- a/lib/signing.ml +++ b/lib/signing.ml @@ -2,7 +2,7 @@ module List = Core.List module Defer = Utils.Defer let digest_to_string lazy_bytes = - Utils.bytes_to_string @@ Hash.digest_bytes @@ Defer.force lazy_bytes + Utils.bytes_to_hex @@ Hash.digest_bytes @@ Defer.force lazy_bytes let sign ~priv ~msg = diff --git a/lib/utils.ml b/lib/utils.ml index 4f2b195..1f11fcd 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -4,6 +4,8 @@ module Char = Core.Char module Int = Core.Int module Lazy = Core.Lazy module Str = Re.Str +module Option = Core.Option +module Base64 = Nocrypto.Base64 module Defer = struct let force = Lazy.force_val @@ -11,11 +13,9 @@ module Defer = struct let bind deferred ~f = lazy (force @@ f @@ force deferred) end -let bytes_of_string string = Cstruct.to_bytes @@ Cstruct.of_hex string - -let bytes_to_string bytes = - Hex.show @@ Hex.of_cstruct @@ Cstruct.of_bytes bytes +let bytes_of_hex string = Cstruct.to_bytes @@ Cstruct.of_hex string +let bytes_to_hex bytes = Hex.show @@ Hex.of_cstruct @@ Cstruct.of_bytes bytes (* 16 hex chars and 128 chars/string length for hash under hex string format *) let _HASH_LENGTH = 128 @@ -47,12 +47,12 @@ let validate_key list = if List.length filtered = _KEY_LENGTH then Some list else None -let to_hex text = "0x" ^ text +let with_hex_prefix text = "0x" ^ text let calculate_index (position, key) = (position * _HEX_SPACE) + key let index_at ~list position = - bytes_to_string @@ Defer.force @@ List.nth_exn list position + bytes_to_hex @@ Defer.force @@ List.nth_exn list position let replace_index ~matrix pairs = List.map pairs ~f:(index_at ~list:matrix) @@ -72,7 +72,7 @@ let verify_with ~matrix ~digest pairs = let concat_hashes left right = left ^ ":" ^ right let char_to_hex_int index char = - let value = char |> Char.to_string |> to_hex |> Int.of_string in + let value = char |> Char.to_string |> with_hex_prefix |> Int.of_string in (index, value) @@ -93,7 +93,7 @@ let pad ~basis msg = let nonzero char = char != nullchar -let unpad msg = String.filter ~f:nonzero msg +let unpad msg = String.filter ~f:nonzero @@ Cstruct.to_string msg let digest_hex_string hex = hex diff --git a/lib/utils.mli b/lib/utils.mli index eec9d7e..b954cb2 100644 --- a/lib/utils.mli +++ b/lib/utils.mli @@ -10,7 +10,7 @@ val _KEY_LENGTH : int val is_hash : string -> bool -val to_hex : string -> string +val with_hex_prefix : string -> string val concat_hashes : string -> string -> string @@ -30,10 +30,10 @@ val verify_with : val pad : basis:int -> string -> Cstruct.t -val unpad : string -> string +val unpad : Cstruct.t -> string -val bytes_of_string : string -> bytes +val bytes_of_hex : string -> bytes -val bytes_to_string : bytes -> string +val bytes_to_hex : bytes -> string val digest_hex_string : string -> string diff --git a/lib/verification.ml b/lib/verification.ml index f75e34c..d310d2b 100644 --- a/lib/verification.ml +++ b/lib/verification.ml @@ -5,7 +5,7 @@ module Defer = Utils.Defer let digest = Utils.digest_hex_string -let delay_string_cast bytes = lazy (Utils.bytes_to_string bytes) +let delay_string_cast bytes = lazy (Utils.bytes_to_hex bytes) let verify ~pub ~msg ~signature = try @@ -21,6 +21,8 @@ let verify ~pub ~msg ~signature = if verified then let fingerprint = Serialization.digest ver_key_bytes in - fingerprint = Utils.bytes_to_string pub + fingerprint = Utils.bytes_to_hex pub else false - with _ -> false + with + | _ -> + false