ligo/vendors/ocaml-secp256k1/src/external.ml

459 lines
16 KiB
OCaml
Raw Normal View History

2018-04-04 12:01:47 +04:00
module Context = struct
type t
2018-04-19 19:10:45 +04:00
external create : int -> t = "caml_secp256k1_context_create"
external clone : t -> t = "caml_secp256k1_context_clone"
external randomize : t -> Bigstring.t -> bool = "caml_secp256k1_context_randomize" [@@noalloc]
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let create ?(sign=true) ?(verify=true) () =
let flags = 1 lor
(if sign then 0x100 else 0) lor
(if verify then 0x200 else 0) in
create flags
2018-04-04 12:01:47 +04:00
let randomize ctx buf =
2018-04-19 19:10:45 +04:00
if Bigstring.length buf < 32 then
invalid_arg "Context.randomize: input must be at least 32 bytes long" ;
2018-04-04 12:01:47 +04:00
randomize ctx buf
end
module Key = struct
type secret
type public
type _ t =
2018-04-19 19:10:45 +04:00
| Sk : Bigstring.t -> secret t
| Pk : Bigstring.t -> public t
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let buffer : type a. a t -> Bigstring.t = function
| Sk sk -> sk
| Pk pk -> pk
2018-04-04 12:01:47 +04:00
let secret_bytes = 32
let public_bytes = 64
2018-04-19 19:10:45 +04:00
let compressed_pk_bytes = 33
let uncompressed_pk_bytes = 65
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let bytes : type a. a t -> int = function
| Sk _ -> secret_bytes
| Pk _ -> public_bytes
let serialized_bytes :
type a. ?compressed:bool -> a t -> int =
fun ?(compressed=true) -> function
| Sk _ -> secret_bytes
| Pk _ -> if compressed then public_bytes + 1 else secret_bytes + 1
2018-04-04 12:01:47 +04:00
let equal : type a. a t -> a t -> bool = fun a b ->
match a, b with
2018-04-19 19:10:45 +04:00
| Sk a, Sk b -> Bigstring.equal a b
| Pk a, Pk b -> Bigstring.equal a b
2018-04-04 12:01:47 +04:00
let copy : type a. a t -> a t = function
2018-04-19 19:10:45 +04:00
| Sk sk -> Sk (Bigstring.copy sk)
| Pk pk -> Pk (Bigstring.copy pk)
external sk_negate_inplace : Context.t -> Bigstring.t -> unit =
"caml_secp256k1_ec_privkey_negate" [@@noalloc]
external sk_add_tweak_inplace : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_privkey_tweak_add" [@@noalloc]
external sk_mul_tweak_inplace : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_privkey_tweak_mul" [@@noalloc]
external pk_negate_inplace : Context.t -> Bigstring.t -> unit =
"caml_secp256k1_ec_pubkey_negate" [@@noalloc]
external pk_add_tweak_inplace : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_pubkey_tweak_add" [@@noalloc]
external pk_mul_tweak_inplace : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_pubkey_tweak_mul" [@@noalloc]
external pk_combine : Context.t -> Bigstring.t -> Bigstring.t list -> bool =
"caml_secp256k1_ec_pubkey_combine" [@@noalloc]
2018-04-04 12:01:47 +04:00
let negate_inplace :
type a. Context.t -> a t -> unit = fun ctx -> function
| Sk k -> sk_negate_inplace ctx k
| Pk k -> pk_negate_inplace ctx k
let negate ctx k =
let k' = copy k in
negate_inplace ctx k' ;
k'
let op_tweak :
2018-04-19 19:10:45 +04:00
type a. string -> (Context.t -> Bigstring.t -> Bigstring.t -> bool) ->
Context.t -> a t -> Bigstring.t -> Bigstring.t =
fun name f ctx k buf ->
let buflen = Bigstring.length buf in
if buflen < 32 then
invalid_arg (Printf.sprintf "Key.%s: " name) ;
let k' = buffer (copy k) in
2018-04-04 12:01:47 +04:00
if not (f ctx k' buf) then
failwith (Printf.sprintf "Key.%s: operation failed" name) ;
k'
let add_tweak :
2018-04-19 19:10:45 +04:00
type a. Context.t -> a t -> Bigstring.t -> a t =
fun ctx k buf ->
2018-04-04 12:01:47 +04:00
match k with
2018-04-19 19:10:45 +04:00
| Sk _ -> Sk (op_tweak "add_tweak" sk_add_tweak_inplace ctx k buf)
| Pk _ -> Pk (op_tweak "add_tweak" pk_add_tweak_inplace ctx k buf)
2018-04-04 12:01:47 +04:00
let mul_tweak :
2018-04-19 19:10:45 +04:00
type a. Context.t -> a t -> Bigstring.t -> a t =
fun ctx k buf ->
2018-04-04 12:01:47 +04:00
match k with
2018-04-19 19:10:45 +04:00
| Sk _ -> Sk (op_tweak "mul_tweak" sk_mul_tweak_inplace ctx k buf)
| Pk _ -> Pk (op_tweak "mul_tweak" pk_mul_tweak_inplace ctx k buf)
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
external pk_parse : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_pubkey_parse" [@@noalloc]
external pk_serialize : Context.t -> Bigstring.t -> Bigstring.t -> int =
"caml_secp256k1_ec_pubkey_serialize" [@@noalloc]
external pk_create : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_pubkey_create" [@@noalloc]
2018-04-04 12:01:47 +04:00
let neuterize :
type a. Context.t -> a t -> public t option = fun ctx -> function
| Pk pk -> Some (Pk pk)
| Sk sk ->
2018-04-19 19:10:45 +04:00
let pk = Bigstring.create public_bytes in
2018-04-04 12:01:47 +04:00
if pk_create ctx pk sk then Some (Pk pk) else None
let neuterize_exn ctx k =
match neuterize ctx k with
| None -> invalid_arg "Key.neuterize_exn: invalid secret key"
| Some pk -> pk
let list_map_filter_opt ~f l =
2018-04-19 19:10:45 +04:00
ListLabels.fold_left ~init:[] ~f:begin fun a e ->
2018-04-04 12:01:47 +04:00
match f e with
| None -> a
| Some r -> r :: a
end l
let combine ctx pks =
let nb_pks = List.length pks in
if nb_pks = 0 || nb_pks > 1024 then None
else
2018-04-19 19:10:45 +04:00
let pk = Bigstring.create public_bytes in
2018-04-04 12:01:47 +04:00
let pks = list_map_filter_opt ~f:begin fun k ->
match neuterize ctx k with
| None -> None
| Some (Pk k) -> Some k
end pks in
if pk_combine ctx pk pks then Some (Pk pk)
else None
let combine_exn ctx pks =
match combine ctx pks with
| None -> invalid_arg "Key.combine_exn: sum of pks is invalid"
| Some pk -> pk
2018-04-19 19:10:45 +04:00
external verify_sk : Context.t -> Bigstring.t -> bool =
"caml_secp256k1_ec_seckey_verify" [@@noalloc]
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let read_sk_exn ctx buf =
let buflen = Bigstring.length buf in
if buflen < secret_bytes then
invalid_arg (Printf.sprintf "Key.read_sk: invalid buffer size %d" buflen) ;
2018-04-04 12:01:47 +04:00
match verify_sk ctx buf with
2018-04-19 19:10:45 +04:00
| true -> Sk Bigstring.(copy (sub buf 0 secret_bytes))
| false -> invalid_arg "Key.read_sk: secret key is invalid"
let read_sk ctx buf =
try Ok (read_sk_exn ctx buf) with
2018-04-04 12:01:47 +04:00
| Invalid_argument msg -> Error msg
2018-04-19 19:10:45 +04:00
let read_pk_exn ctx buf =
let buflen = Bigstring.length buf in
if buflen < compressed_pk_bytes then
invalid_arg (Printf.sprintf "Key.read_pk: invalid buffer size %d" buflen) ;
let outbuf = Bigstring.create public_bytes in
if pk_parse ctx outbuf buf then
Pk outbuf
else
invalid_arg "Key.read_pk_exn: public key is invalid"
let read_pk ctx buf =
try Ok (read_pk_exn ctx buf) with
2018-04-04 12:01:47 +04:00
| Invalid_argument msg -> Error msg
let write :
2018-04-19 19:10:45 +04:00
type a. ?compress:bool -> Context.t -> ?pos:int -> Bigstring.t -> a t -> int =
2018-04-04 12:01:47 +04:00
fun ?(compress=true) ctx ?(pos=0) buf -> function
| Sk sk ->
2018-04-19 19:10:45 +04:00
let buflen = Bigstring.length buf in
2018-04-04 12:01:47 +04:00
if pos < 0 || pos > buflen - secret_bytes then
invalid_arg "Key.write (secret): pos < 0 or pos + 32 > buflen" ;
2018-04-19 19:10:45 +04:00
Bigstring.blit sk 0 buf pos secret_bytes ;
2018-04-04 12:01:47 +04:00
secret_bytes
| Pk pk ->
2018-04-19 19:10:45 +04:00
let buflen = Bigstring.length buf in
2018-04-04 12:01:47 +04:00
if pos < 0
2018-04-19 19:10:45 +04:00
|| (compress && pos > buflen - compressed_pk_bytes)
|| (not compress && pos > buflen - uncompressed_pk_bytes) then
2018-04-04 12:01:47 +04:00
invalid_arg (Printf.sprintf "Key.write (public): pos=%d, buflen=%d" pos buflen) ;
let len = if compress then 33 else 65 in
2018-04-19 19:10:45 +04:00
let buf = Bigstring.sub buf pos len in
2018-04-04 12:01:47 +04:00
pk_serialize ctx buf pk
let to_bytes :
2018-04-19 19:10:45 +04:00
type a. ?compress:bool -> Context.t -> a t -> Bigstring.t =
2018-04-04 12:01:47 +04:00
fun ?(compress=true) ctx -> function
| Sk _ as sk ->
2018-04-19 19:10:45 +04:00
let buf = Bigstring.create secret_bytes in
2018-04-04 12:01:47 +04:00
let _ = write ~compress ctx buf sk in
buf
| Pk _ as pk ->
let buf =
2018-04-19 19:10:45 +04:00
Bigstring.create (1 + (if compress then secret_bytes else public_bytes)) in
2018-04-04 12:01:47 +04:00
let _ = write ~compress ctx buf pk in
buf
end
module Sign = struct
type plain
type recoverable
type _ t =
2018-04-19 19:10:45 +04:00
| P : Bigstring.t -> plain t
| R : Bigstring.t -> recoverable t
let buffer : type a. a t -> Bigstring.t = function
| P plain -> plain
| R recoverable -> recoverable
2018-04-04 12:01:47 +04:00
let plain_bytes = 64
let recoverable_bytes = 65
let msg_bytes = 32
let equal : type a. a t -> a t -> bool = fun a b ->
match a, b with
2018-04-19 19:10:45 +04:00
| P a, P b -> Bigstring.equal a b
| R a, R b -> Bigstring.equal a b
external parse_compact : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_signature_parse_compact" [@@noalloc]
external parse_der : Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_signature_parse_der" [@@noalloc]
external serialize_compact : Context.t -> Bigstring.t -> Bigstring.t -> unit =
"caml_secp256k1_ecdsa_signature_serialize_compact" [@@noalloc]
external serialize_der : Context.t -> Bigstring.t -> Bigstring.t -> int =
"caml_secp256k1_ecdsa_signature_serialize_der" [@@noalloc]
external parse_recoverable : Context.t -> Bigstring.t -> Bigstring.t -> int -> bool =
"caml_secp256k1_ecdsa_recoverable_signature_parse_compact" [@@noalloc]
external serialize_recoverable : Context.t -> Bigstring.t -> Bigstring.t -> int =
"caml_secp256k1_ecdsa_recoverable_signature_serialize_compact" [@@noalloc]
let read_exn ctx buf =
let buflen = Bigstring.length buf in
if buflen < plain_bytes then
invalid_arg (Printf.sprintf "Sign.read: invalid buffer size %d" buflen) ;
let signature = Bigstring.create plain_bytes in
if parse_compact ctx signature buf then
2018-04-04 12:01:47 +04:00
P signature
2018-04-19 19:10:45 +04:00
else
invalid_arg "Sign.read: signature could not be parsed"
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let read ctx buf =
try Ok (read_exn ctx buf) with
Invalid_argument msg -> Error msg
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let read_der_exn ctx buf =
let signature = Bigstring.create plain_bytes in
if parse_der ctx signature buf then
2018-04-04 12:01:47 +04:00
P signature
2018-04-19 19:10:45 +04:00
else
invalid_arg "Sign.read_der: signature could not be parsed"
let read_der ctx buf =
try Ok (read_der_exn ctx buf) with
Invalid_argument msg -> Error msg
let read_recoverable_exn ctx buf =
let buflen = Bigstring.length buf in
if buflen < recoverable_bytes then
invalid_arg (Printf.sprintf "Sign.read_recoverable: invalid buffer size %d" buflen) ;
let signature = Bigstring.create recoverable_bytes in
let recid = int_of_char (Bigstring.get buf 64) in
if parse_recoverable ctx signature buf recid then
R signature
else
invalid_arg "Sign.read_recoverable: signature could not be parsed"
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let read_recoverable ctx buf =
try Ok (read_recoverable_exn ctx buf) with
2018-04-04 12:01:47 +04:00
| Invalid_argument msg -> Error msg
let write_exn :
2018-04-19 19:10:45 +04:00
type a. ?der:bool -> Context.t -> Bigstring.t -> a t -> int =
fun ?(der=false) ctx buf -> function
2018-04-04 12:01:47 +04:00
| P signature ->
2018-04-19 19:10:45 +04:00
let buflen = Bigstring.length buf in
if not der then begin
if buflen < plain_bytes then
invalid_arg (Printf.sprintf "Sign.write: buffer length too small (%d)" buflen) ;
serialize_compact ctx buf signature ;
plain_bytes
end
else begin
match serialize_der ctx buf signature with
| 0 -> invalid_arg "Sign.write_exn: buffer too small to \
contain a DER signature"
| len -> len
end
2018-04-04 12:01:47 +04:00
| R signature ->
2018-04-19 19:10:45 +04:00
let buflen = Bigstring.length buf in
if buflen < recoverable_bytes then
invalid_arg (Printf.sprintf "Sign.write: buffer length too small (%d)" buflen) ;
let recid = serialize_recoverable ctx buf signature in
Bigstring.set buf 64 (char_of_int recid) ;
recoverable_bytes
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let write ?der ctx buf signature =
try Ok (write_exn ?der ctx buf signature) with
Invalid_argument msg -> Error msg
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let to_bytes :
type a. ?der:bool -> Context.t -> a t -> Bigstring.t =
fun ?(der=false) ctx -> function
| P _ as signature ->
if der then begin
let buf = Bigstring.create 72 in
let nb_written = write_exn ~der ctx buf signature in
Bigstring.sub buf 0 nb_written
end
else
let buf = Bigstring.create plain_bytes in
let _nb_written = write_exn ~der ctx buf signature in
buf
| R _ as signature ->
2018-05-03 17:36:02 +04:00
let buf = Bigstring.create recoverable_bytes in
let _nb_written = write_exn ctx buf signature in
buf
2018-04-19 19:10:45 +04:00
external normalize :
Context.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_signature_normalize" [@@noalloc]
let normalize ctx (P signature) =
let normalized_sig = Bigstring.create plain_bytes in
if normalize ctx normalized_sig signature then
Some (P normalized_sig) else None
(* [sign ctx signature msg sk] *)
external sign :
Context.t -> Bigstring.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_sign" [@@noalloc]
(* [verify ctx pk msg signature] *)
external verify :
Context.t -> Bigstring.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_verify" [@@noalloc]
let check_msglen msg =
let msglen = Bigstring.length msg in
if msglen < msg_bytes
then invalid_arg
(Printf.sprintf "message is too small (%d < %d)" msglen msg_bytes)
let sign_exn ctx buf ~sk ~msg =
check_msglen msg ;
let buflen = Bigstring.length buf in
if buflen < plain_bytes then
invalid_arg (Printf.sprintf "Sign.write_sign: buffer length too \
small (%d)" buflen) ;
match sign ctx buf (Key.buffer sk) msg with
| true -> ()
| false -> invalid_arg "Sign.write_sign: the nonce generation \
function failed, or the private key was \
invalid"
let write_sign_exn ctx buf ~sk ~msg =
let signature = Bigstring.create plain_bytes in
sign_exn ctx signature ~sk ~msg ;
write_exn ctx buf (P signature)
let write_sign ctx buf ~sk ~msg =
try Ok (write_sign_exn ctx ~sk ~msg buf)
with Invalid_argument msg -> Error msg
let sign_exn ctx ~sk msg =
let signature = Bigstring.create plain_bytes in
sign_exn ctx signature ~sk ~msg ;
P signature
let sign ctx ~sk msg =
try Ok (sign_exn ctx ~sk msg)
with Invalid_argument msg -> Error msg
external sign_recoverable :
Context.t -> Bigstring.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_sign_recoverable" [@@noalloc]
let write_sign_recoverable_exn ctx ~sk ~msg buf =
check_msglen msg ;
let buflen = Bigstring.length buf in
if buflen < recoverable_bytes then
invalid_arg (Printf.sprintf "Sign.write_sign_recoverable: buffer \
length too small (%d)" buflen) ;
if sign_recoverable ctx buf (Key.buffer sk) msg then
recoverable_bytes
else invalid_arg "Sign.write_sign_recoverable_exn: the nonce \
generation function failed, or the private key \
was invalid"
let write_sign_recoverable ctx ~sk ~msg buf =
try Ok (write_sign_recoverable_exn ctx ~sk ~msg buf)
with Invalid_argument msg -> Error msg
2018-04-04 12:01:47 +04:00
let sign_recoverable ctx ~sk msg =
2018-04-19 19:10:45 +04:00
let signature = Bigstring.create recoverable_bytes in
2018-04-04 12:01:47 +04:00
match write_sign_recoverable ctx ~sk ~msg signature with
| Error error -> Error error
| Ok _nb_written -> Ok (R signature)
let sign_recoverable_exn ctx ~sk msg =
match sign_recoverable ctx ~sk msg with
| Error msg -> invalid_arg msg
| Ok signature -> signature
2018-04-19 19:10:45 +04:00
external to_plain : Context.t -> Bigstring.t -> Bigstring.t -> unit =
"caml_secp256k1_ecdsa_recoverable_signature_convert" [@@noalloc]
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let to_plain : type a. Context.t -> a t -> plain t = fun ctx -> function
| P _ as signature -> signature
| R recoverable ->
let plain = Bigstring.create plain_bytes in
to_plain ctx plain recoverable ;
P plain
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let verify_plain_exn ctx ~pk msg signature =
check_msglen msg ;
let siglen = Bigstring.length signature in
if siglen < plain_bytes then
invalid_arg (Printf.sprintf "verify: signature too short (%d < %d)"
siglen plain_bytes) ;
verify ctx (Key.buffer pk) msg signature
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let verify_exn ctx ~pk ~msg ~signature =
let P signature = to_plain ctx signature in
verify_plain_exn ctx ~pk msg signature
2018-04-04 12:01:47 +04:00
let verify ctx ~pk ~msg ~signature =
try Ok (verify_exn ctx ~pk ~msg ~signature) with
| Invalid_argument msg -> Error msg
2018-04-19 19:10:45 +04:00
external recover :
Context.t -> Bigstring.t -> Bigstring.t -> Bigstring.t -> bool =
"caml_secp256k1_ecdsa_recover" [@@noalloc]
2018-04-04 12:01:47 +04:00
2018-04-19 19:10:45 +04:00
let recover_exn ctx ~signature:(R signature) msg =
check_msglen msg ;
let pk = Bigstring.create Key.public_bytes in
2018-04-04 12:01:47 +04:00
if recover ctx pk signature msg then Key.Pk pk
else
invalid_arg "Sign.recover: pk could not be recovered"
2018-04-19 19:10:45 +04:00
let recover ctx ~signature msg =
try Ok (recover_exn ctx ~signature msg) with
2018-05-03 17:36:02 +04:00
Invalid_argument msg -> Error msg
2018-04-04 12:01:47 +04:00
end