package bip32

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file bip32.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
(*---------------------------------------------------------------------------
   Copyright (c) 2017 Vincent Bernardoff. All rights reserved.
   Distributed under the ISC license, see terms at the end of the file.
  ---------------------------------------------------------------------------*)

open Secp256k1

module type CRYPTO = sig
  val sha256 : Cstruct.t -> Cstruct.t
  val ripemd160 : Cstruct.t -> Cstruct.t
  val hmac_sha512 : key:Cstruct.t -> Cstruct.t -> Cstruct.t

  val ctx : Context.t
end

let hardened i =
  Int32.logand i 0x8000_0000l <> 0l

let pp_print_path ppf i =
  if hardened i then
    Format.fprintf ppf "%ld'" Int32.(logand i 0x7fff_ffffl)
  else
    Format.fprintf ppf "%ld" i

type 'a t = {
  k : 'a Key.t ;
  c : Cstruct.t ;
  path : Int32.t list ;
  parent : Cstruct.t ;
}

let create_key ?(parent=Cstruct.create 20) k c path = { k ; c ; path ; parent }

module type S = sig
  val pp : Format.formatter -> _ t -> unit
  val of_entropy : Cstruct.t -> (Key.secret t, string) result
  val of_entropy_exn : Cstruct.t -> Key.secret t
  val neuterize : _ t -> Key.public t
  val derive : 'a t -> Int32.t -> 'a t
  val derive_path : 'a t -> Int32.t list -> 'a t

  val secret_of_bytes : Cstruct.t -> Key.secret t option
  val secret_of_bytes_exn : Cstruct.t -> Key.secret t
  val public_of_bytes : Cstruct.t -> Key.public t option
  val public_of_bytes_exn : Cstruct.t -> Key.public t
  val to_cstruct : 'a t -> Cstruct.t
end

module Make (Crypto : CRYPTO) = struct
  open Crypto
  let fingerprint k =
    Key.to_bytes ~compress:true ctx k |>
    Cstruct.of_bigarray |>
    Crypto.sha256 |>
    Crypto.ripemd160

  let pp_k ppf k =
    Hex.pp ppf (Key.to_bytes ctx k |> Cstruct.of_bigarray |> Hex.of_cstruct)

  let pp :
    type a. Format.formatter -> a t -> unit = fun ppf { k ; c ; path ; parent } ->
    Format.fprintf ppf "@[<hov 0>key %a@ chaincode %a@ path %a@ parent %a@]"
      pp_k k
      Hex.pp (Hex.of_cstruct c)
      (Format.pp_print_list
         ~pp_sep:(fun ppf () -> Format.pp_print_char ppf '/')
         pp_print_path) (List.rev path)
      Hex.pp (Hex.of_cstruct parent)

  let of_entropy entropy =
    let key = Cstruct.of_string "Bitcoin seed" in
    let m = Crypto.hmac_sha512 ~key entropy in
    match Key.read_sk ctx m.buffer with
    | Error msg -> Error msg
    | Ok k -> Ok (create_key k (Cstruct.sub m 32 32) [])

  let of_entropy_exn entropy =
    match of_entropy entropy with
    | Error msg -> invalid_arg msg
    | Ok sk -> sk

  let neuterize : type a. a t -> Key.public t = fun k ->
    { k with k = Key.neuterize_exn ctx k.k }

  let derive : type a. a t -> Int32.t -> a t = fun { k ; c = key ; path } i ->
      match k, hardened i with
      | Key.Pk _, true ->
        invalid_arg "derive: cannot derive hardened index" ;
      | Key.Sk _, _ ->
        let buf = Cstruct.create 37 in
        let (_:int) = if hardened i then
          Key.write ctx buf.buffer ~pos:1 k
        else begin
          let pk = Key.neuterize_exn ctx k in
          Key.write ~compress:true ctx buf.buffer pk
        end in () ;
        Cstruct.BE.set_uint32 buf 33 i ;
        let derived = Crypto.hmac_sha512 ~key buf in
        let k' = Key.add_tweak ctx k derived.buffer in
        let c' = Cstruct.sub derived 32 32 in
        create_key ~parent:(fingerprint (Key.neuterize_exn ctx k)) k' c' (i :: path)
      | Key.Pk _, _ ->
        let cs = Cstruct.create 37 in
        let (_:int) = Key.write ~compress:true ctx cs.buffer k in
        let derived = Crypto.hmac_sha512 ~key cs in
        let k' = Key.add_tweak ctx k derived.buffer in
        let c' = Cstruct.sub derived 32 32 in
        create_key ~parent:(fingerprint k) k' c' (i :: path)

  let derive_path :
    type a. a t -> Int32.t list -> a t = fun k path ->
    ListLabels.fold_left path ~init:k ~f:derive

  let secret_of_bytes_exn cs =
    let _depth = Cstruct.get_uint8 cs 0 in
    let parent = Cstruct.sub cs 1 4 in
    let child_number = Cstruct.BE.get_uint32 cs 5 in
    let chaincode = Cstruct.sub cs 9 32 in
    let secret = Key.read_sk_exn ctx cs.buffer ~pos:41 in
    create_key ~parent secret chaincode [child_number]

  let public_of_bytes_exn cs =
    let _depth = Cstruct.get_uint8 cs 0 in
    let parent = Cstruct.sub cs 1 4 in
    let child_number = Cstruct.BE.get_uint32 cs 5 in
    let chaincode = Cstruct.sub cs 9 32 in
    let public = Key.read_pk_exn ctx cs.buffer ~pos:40 in
    create_key ~parent public chaincode [child_number]

  let secret_of_bytes cs =
    try Some (secret_of_bytes_exn cs) with _ -> None
  let public_of_bytes cs =
    try Some (public_of_bytes_exn cs) with _ -> None

  let secret_of_bytes_exn cs =
    match secret_of_bytes cs with
    | None -> invalid_arg "secret_of_bytes_exn"
    | Some sk -> sk

  let public_of_bytes_exn cs =
    match public_of_bytes cs with
    | None -> invalid_arg "public_of_bytes_exn"
    | Some pk -> pk

  let to_cstruct : type a. a t -> Cstruct.t = fun { k ; c ; path ; parent } ->
    let buf = Cstruct.create 74 in
    Cstruct.set_uint8 buf 0 (List.length path) ;
    Cstruct.blit parent 0 buf 1 4 ;
    Cstruct.BE.set_uint32 buf 5 (match path with [] -> 0l  | i :: _ -> i) ;
    Cstruct.blit c 0 buf 9 32 ;
    let _nb_written = begin match k with
      | Key.Sk _ -> Key.write ctx buf.buffer ~pos:42 k
      | Key.Pk _ -> Key.write ~compress:true ctx buf.buffer ~pos:41 k
    end in
    buf
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.
  ---------------------------------------------------------------------------*)