(*--------------------------------------------------------------------------- Copyright (c) 2017 Vincent Bernardoff. All rights reserved. Distributed under the ISC license, see terms at the end of the file. ---------------------------------------------------------------------------*) module Rand = struct external randombytes : Cstruct.buffer -> int -> unit = "ml_randombytes" [@@noalloc] let gen sz = let cs = Cstruct.create_unsafe sz in randombytes (Cstruct.to_bigarray cs) sz ; cs let write cs = Cstruct.(randombytes (to_bigarray cs) (len cs)) end module Hash = struct let bytes = 64 external sha512 : Cstruct.buffer -> Cstruct.buffer -> int -> unit = "ml_crypto_hash" [@@noalloc] let sha512 msg = let q = Cstruct.create_unsafe bytes in sha512 q.buffer msg.Cstruct.buffer (Cstruct.len msg) ; q end let cs_of_z cs z = Cstruct.memset cs 0 ; let bits = Z.to_bits z in Cstruct.blit_from_string bits 0 cs 0 (String.length bits) let unopt_invalid_arg1 ~msg f cs = match f cs with | Some v -> v | None -> invalid_arg msg module Nonce = struct type t = Cstruct.t let bytes = 24 let gen () = Rand.gen bytes let rec incr_byte b step byteno = let res = Cstruct.BE.get_uint16 b byteno + step in let lo = res land 0xffff in let hi = res asr 16 in Cstruct.BE.set_uint16 b byteno lo ; if hi = 0 || byteno = 0 then () else incr_byte b hi (byteno - 2) let increment ?(step = 1) nonce = let new_nonce = Cstruct.create_unsafe 24 in Cstruct.blit nonce 0 new_nonce 0 24 ; incr_byte new_nonce step 22 ; new_nonce let of_cstruct cs = try Some (Cstruct.sub cs 0 bytes) with _ -> None let of_cstruct_exn = unopt_invalid_arg1 ~msg:"Box.Nonce.of_cstruct_exn" of_cstruct let to_cstruct nonce = nonce end module Secretbox = struct type key = Cstruct.t let keybytes = 32 let zerobytes = 32 let boxzerobytes = 16 let genkey () = Rand.gen 32 let of_cstruct cs = if Cstruct.len cs < keybytes then None else Some (Cstruct.sub cs 0 keybytes) let of_cstruct_exn = unopt_invalid_arg1 ~msg:"Secret_box.of_cstruct_exn" of_cstruct external secretbox : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> unit = "ml_secretbox" [@@noalloc] external secretbox_open : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> int = "ml_secretbox_open" [@@noalloc] let box ~key ~nonce ~msg = let msglen = Cstruct.len msg in let buflen = msglen + zerobytes in let buf = Cstruct.create buflen in Cstruct.blit msg 0 buf zerobytes msglen ; secretbox buf.buffer buf.buffer nonce.Cstruct.buffer key.Cstruct.buffer ; Cstruct.sub buf boxzerobytes (buflen - boxzerobytes) let box_noalloc ~key ~nonce ~msg = secretbox msg.Cstruct.buffer msg.buffer nonce.Cstruct.buffer key.Cstruct.buffer let box_open ~key ~nonce ~cmsg = let msglen = Cstruct.len cmsg - boxzerobytes in let buf = Cstruct.create (zerobytes + msglen) in Cstruct.blit cmsg 0 buf boxzerobytes (msglen + boxzerobytes) ; match secretbox_open buf.buffer buf.buffer nonce.Cstruct.buffer key.Cstruct.buffer with | 0 -> Some (Cstruct.sub buf zerobytes msglen) | _ -> None let box_open_noalloc ~key ~nonce ~cmsg = match secretbox_open cmsg.Cstruct.buffer cmsg.buffer nonce.Cstruct.buffer key.Cstruct.buffer with | 0 -> true | _ -> false end module Box = struct type secret type public type combined type _ key = | Sk : Cstruct.t -> secret key | Pk : Cstruct.t -> public key | Ck : Cstruct.t -> combined key let skbytes = 32 let pkbytes = 32 let beforenmbytes = 32 let zerobytes = 32 let boxzerobytes = 16 let to_cstruct : type a. a key -> Cstruct.t = function | Pk cs -> cs | Sk cs -> cs | Ck cs -> cs let blit_to_cstruct : type a. a key -> ?pos:int -> Cstruct.t -> unit = fun key ?(pos=0) cs -> match key with | Pk pk -> Cstruct.blit pk 0 cs pos pkbytes | Sk sk -> Cstruct.blit sk 0 cs pos skbytes | Ck ck -> Cstruct.blit ck 0 cs pos beforenmbytes let pp : type a. Format.formatter -> a key -> unit = fun ppf -> function | Pk cs -> Format.fprintf ppf "P %a" Hex.pp (Hex.of_cstruct cs) | Sk cs -> Format.fprintf ppf "S %a" Hex.pp (Hex.of_cstruct cs) | Ck cs -> Format.fprintf ppf "C %a" Hex.pp (Hex.of_cstruct cs) let show t = Format.asprintf "%a" pp t let equal : type a. a key -> a key -> bool = fun a b -> match a, b with | Pk a, Pk b -> Cstruct.equal a b | Sk a, Sk b -> Cstruct.equal a b | Ck a, Ck b -> Cstruct.equal a b let sk_of_cstruct cs = try Some (Sk (Cstruct.sub cs 0 skbytes)) with _ -> None let pk_of_cstruct cs = try Some (Pk (Cstruct.sub cs 0 pkbytes)) with _ -> None let ck_of_cstruct cs = try Some (Ck (Cstruct.sub cs 0 beforenmbytes)) with _ -> None let sk_of_cstruct_exn = unopt_invalid_arg1 ~msg:"Box.sk_of_cstruct_exn" sk_of_cstruct let pk_of_cstruct_exn = unopt_invalid_arg1 ~msg:"Box.pk_of_cstruct_exn" pk_of_cstruct let ck_of_cstruct_exn = unopt_invalid_arg1 ~msg:"Box.ck_of_cstruct_exn" ck_of_cstruct external keypair : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_box_keypair" [@@noalloc] let keypair () = let sk = Cstruct.create skbytes in let pk = Cstruct.create pkbytes in keypair pk.buffer sk.buffer ; Pk pk, Sk sk external box_stub : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_box" [@@noalloc] let box ~pk:(Pk pk) ~sk:(Sk sk) ~nonce ~msg = let msglen = Cstruct.len msg in let buflen = msglen + zerobytes in let buf = Cstruct.create buflen in Cstruct.blit msg 0 buf zerobytes msglen ; box_stub buf.buffer buf.buffer nonce.Cstruct.buffer pk.buffer sk.buffer ; Cstruct.sub buf boxzerobytes (buflen - boxzerobytes) let box_noalloc ~pk:(Pk pk) ~sk:(Sk sk) ~nonce ~msg = box_stub msg.Cstruct.buffer msg.buffer nonce.Cstruct.buffer pk.buffer sk.buffer external box_open_stub : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> int = "ml_crypto_box_open" [@@noalloc] let box_open ~pk:(Pk pk) ~sk:(Sk sk) ~nonce ~cmsg = let msglen = Cstruct.len cmsg - boxzerobytes in let buf = Cstruct.create (zerobytes + msglen) in Cstruct.blit cmsg 0 buf boxzerobytes (msglen + boxzerobytes) ; match box_open_stub buf.buffer buf.buffer nonce.Cstruct.buffer pk.buffer sk.buffer with | 0 -> Some (Cstruct.sub buf zerobytes msglen) | _ -> None let box_open_noalloc ~pk:(Pk pk) ~sk:(Sk sk) ~nonce ~cmsg = match box_open_stub cmsg.Cstruct.buffer cmsg.buffer nonce.Cstruct.buffer pk.buffer sk.buffer with | 0 -> true | _ -> false external box_beforenm : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_box_beforenm" [@@noalloc] let combine (Pk pk) (Sk sk) = let combined = Cstruct.create_unsafe beforenmbytes in box_beforenm combined.buffer pk.buffer sk.buffer ; Ck combined external box_afternm : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_box_afternm" [@@noalloc] let box_combined ~k:(Ck k) ~nonce ~msg = let msglen = Cstruct.len msg in let buflen = msglen + zerobytes in let buf = Cstruct.create buflen in Cstruct.blit msg 0 buf zerobytes msglen ; box_afternm buf.buffer buf.buffer nonce.Cstruct.buffer k.buffer ; Cstruct.sub buf boxzerobytes (buflen - boxzerobytes) let box_combined_noalloc ~k:(Ck k) ~nonce ~msg = box_afternm msg.Cstruct.buffer msg.buffer nonce.Cstruct.buffer k.buffer external box_open_afternm : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> int = "ml_crypto_box_open_afternm" [@@noalloc] let box_open_combined ~k:(Ck k) ~nonce ~cmsg = let msglen = Cstruct.len cmsg - boxzerobytes in let buflen = msglen + zerobytes in let buf = Cstruct.create buflen in Cstruct.blit cmsg 0 buf boxzerobytes (msglen + boxzerobytes) ; match box_open_afternm buf.buffer buf.buffer nonce.Cstruct.buffer k.buffer with | 0 -> Some (Cstruct.sub buf zerobytes msglen) | _ -> None let box_open_combined_noalloc ~k:(Ck k) ~nonce ~cmsg = match box_open_afternm cmsg.Cstruct.buffer cmsg.buffer nonce.Cstruct.buffer k.buffer with | 0 -> true | _ -> false end module Sign = struct type secret type extended type public type _ key = | Sk : Cstruct.t -> secret key | Ek : Cstruct.t -> extended key | Pk : Cstruct.t -> public key let bytes = 64 let pkbytes = 32 let skbytes = 64 let ekbytes = 64 let seedbytes = 32 let sk_of_cstruct cs = try Some (Sk (Cstruct.sub cs 0 skbytes)) with _ -> None let ek_of_cstruct cs = try Some (Ek (Cstruct.sub cs 0 ekbytes)) with _ -> None let pk_of_cstruct cs = try Some (Pk (Cstruct.sub cs 0 pkbytes)) with _ -> None let sk_of_cstruct_exn = unopt_invalid_arg1 ~msg:"Sign.sk_of_cstruct_exn" sk_of_cstruct let ek_of_cstruct_exn = unopt_invalid_arg1 ~msg:"Sign.ek_of_cstruct_exn" ek_of_cstruct let pk_of_cstruct_exn = unopt_invalid_arg1 ~msg:"Sign.pk_of_cstruct_exn" pk_of_cstruct let to_cstruct : type a. a key -> Cstruct.t = function | Pk cs -> cs | Sk cs -> cs | Ek cs -> cs let seed (Sk cs) = Cstruct.sub cs 0 seedbytes let blit_to_cstruct : type a. a key -> ?pos:int -> Cstruct.t -> unit = fun key ?(pos=0) cs -> match key with | Pk pk -> Cstruct.blit pk 0 cs pos pkbytes | Sk sk -> Cstruct.blit sk 0 cs pos skbytes | Ek ek -> Cstruct.blit ek 0 cs pos ekbytes let pp : type a. Format.formatter -> a key -> unit = fun ppf -> function | Pk cs -> Format.fprintf ppf "P %a" Hex.pp (Hex.of_cstruct cs) | Sk cs -> Format.fprintf ppf "S %a" Hex.pp (Hex.of_cstruct cs) | Ek cs -> Format.fprintf ppf "E %a" Hex.pp (Hex.of_cstruct cs) let show t = Format.asprintf "%a" pp t let equal : type a. a key -> a key -> bool = fun a b -> match a, b with | Pk a, Pk b -> Cstruct.equal a b | Sk a, Sk b -> Cstruct.equal a b | Ek a, Ek b -> Cstruct.equal a b external keypair : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_sign_keypair" [@@noalloc] external keypair_seed : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_sign_keypair_seed" [@@noalloc] let keypair ?seed () = let pk = Cstruct.create_unsafe pkbytes in let sk = Cstruct.create_unsafe skbytes in begin match seed with | None -> Cstruct.(keypair (to_bigarray pk) (to_bigarray sk)) | Some cs -> if Cstruct.len cs < seedbytes then invalid_arg "Sign.keypair: seed must be at least 32 bytes long" ; Cstruct.blit cs 0 sk 0 pkbytes ; Cstruct.(keypair_seed (to_bigarray pk) (to_bigarray sk)) end ; Pk pk, Sk sk let extended (Sk sk) = let cs = Hash.sha512 (Cstruct.sub sk 0 pkbytes) in Cstruct.(set_uint8 cs 0 (get_uint8 cs 0 land 248)) ; Cstruct.(set_uint8 cs 31 (get_uint8 cs 31 land 127)) ; Cstruct.(set_uint8 cs 31 (get_uint8 cs 31 lor 64)) ; Ek cs external sign : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_sign" [@@noalloc] external sign_extended : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_crypto_sign_extended" [@@noalloc] let sign ~key:(Sk sk) msg = let msglen = Cstruct.len msg in let cs = Cstruct.create_unsafe (bytes + msglen) in Cstruct.blit msg 0 cs bytes msglen ; Cstruct.(sign (to_bigarray cs) (to_bigarray sk)) ; cs let sign_extended ~key:(Ek ek) msg = let msglen = Cstruct.len msg in let cs = Cstruct.create_unsafe (bytes + msglen) in Cstruct.blit msg 0 cs bytes msglen ; Cstruct.(sign_extended (to_bigarray cs) (to_bigarray ek)) ; cs let detached ~key msg = Cstruct.sub (sign ~key msg) 0 bytes let detached_extended ~key msg = Cstruct.sub (sign_extended ~key msg) 0 bytes external verify : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> int = "ml_crypto_sign_open" [@@noalloc] let verify ~key:(Pk pk) smsg = let mlen = Cstruct.create_unsafe 8 in let msg = Cstruct.(create (len smsg)) in let ret = Cstruct.(verify (to_bigarray msg) (to_bigarray mlen) (to_bigarray smsg) (to_bigarray pk)) in match ret with | 0 -> let len = Cstruct.LE.get_uint64 mlen 0 |> Int64.to_int in Some (Cstruct.sub msg 0 len) | _ -> None let verify_detached ~key ~signature msg = let cs = Cstruct.create_unsafe (bytes + Cstruct.len msg) in Cstruct.blit signature 0 cs 0 bytes ; Cstruct.blit msg 0 cs bytes (Cstruct.len msg) ; match verify ~key cs with | None -> false | Some _ -> true external add : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_add" [@@noalloc] let add (Pk p) (Pk q) = let cs = Cstruct.create_unsafe pkbytes in Cstruct.blit p 0 cs 0 pkbytes ; Cstruct.(add (to_bigarray cs) (to_bigarray q)) ; Pk cs external mult : Cstruct.buffer -> Cstruct.buffer -> Cstruct.buffer -> unit = "ml_scalarmult" [@@noalloc] external base : Cstruct.buffer -> Cstruct.buffer -> unit = "ml_scalarbase" [@@noalloc] let mult (Pk q) s = let r = Cstruct.create_unsafe pkbytes in let scalar = Cstruct.create_unsafe pkbytes in cs_of_z scalar s ; Cstruct.(mult (to_bigarray r) (to_bigarray q) (to_bigarray scalar)) ; Pk r let base_direct s = let cs = Cstruct.create_unsafe pkbytes in Cstruct.(base (to_bigarray cs) (to_bigarray s)) ; cs let base s = let r = Cstruct.create_unsafe pkbytes in let scalar = Cstruct.create_unsafe pkbytes in cs_of_z scalar s ; Cstruct.(base (to_bigarray r) (to_bigarray scalar)) ; Pk r let public : type a. a key -> public key = function | Pk _ as pk -> pk | Sk cs -> Pk (Cstruct.sub cs 32 32) | Ek cs -> Pk (base_direct (Cstruct.sub cs 0 32)) end (*--------------------------------------------------------------------------- Copyright (c) 2017 Vincent Bernardoff Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ---------------------------------------------------------------------------*)