Signer/Encrypted: use custom Base58Check prefixes

This commit is contained in:
Vincent Bernardoff 2018-06-19 22:22:17 +02:00 committed by Benjamin Canou
parent dee9c470fd
commit e92e1aee17
4 changed files with 124 additions and 35 deletions

View File

@ -320,6 +320,11 @@ module Prefix = struct
let secp256k1_secret_key = "\017\162\224\201" (* spsk(54) *)
let p256_secret_key = "\016\081\238\189" (* p2sk(54) *)
(* 56 *)
let ed25519_encrypted_seed = "\007\090\060\179\041" (* edesk(88) *)
let secp256k1_encrypted_secret_key = "\009\237\241\174\150" (* spesk(88) *)
let p256_encrypted_secret_key = "\009\048\057\115\171" (* p2esk(88) *)
(* 33 *)
let secp256k1_public_key = "\003\254\226\086" (* sppk(55) *)
let p256_public_key = "\003\178\139\127" (* p2pk(55) *)

View File

@ -31,6 +31,10 @@ module Prefix : sig
val p256_public_key: string
val p256_secret_key: string
val p256_signature: string
val ed25519_encrypted_seed: string
val secp256k1_encrypted_secret_key: string
val p256_encrypted_secret_key: string
val generic_signature: string
val chain_id: string

View File

@ -7,6 +7,10 @@
(* *)
(**************************************************************************)
type Base58.data += Encrypted_ed25519 of MBytes.t
type Base58.data += Encrypted_secp256k1 of MBytes.t
type Base58.data += Encrypted_p256 of MBytes.t
open Client_keys
let scheme = "encrypted"
@ -19,31 +23,99 @@ module Raw = struct
(* Fixed zero nonce *)
let nonce = Crypto_box.zero_nonce
(* Secret keys for Ed25519, secp256k1, P256 are 32 bytes long. *)
let encrypted_size = Crypto_box.boxzerobytes + 32
let pbkdf ~salt ~password =
Cstruct.to_bigarray
(Pbkdf.pbkdf2 ~prf:`SHA512 ~count:2048 ~dk_len:32l
(Pbkdf.pbkdf2 ~prf:`SHA512 ~count:32768 ~dk_len:32l
~salt: (Cstruct.of_bigarray salt)
~password: (Cstruct.of_bigarray password))
let encrypt ~password sk =
let salt = Rand.generate salt_len in
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~password ~salt) in
let msg = Data_encoding.Binary.to_bytes_exn Signature.Secret_key.encoding sk in
let encrypted_passwd = Crypto_box.Secretbox.box key msg nonce in
MBytes.concat "" [ salt ; encrypted_passwd ]
let decrypt ~password ~encrypted_sk =
let len = MBytes.length encrypted_sk in
let salt = MBytes.sub encrypted_sk 0 salt_len in
let encrypted_sk = MBytes.sub encrypted_sk salt_len (len - salt_len) in
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~salt ~password) in
match Crypto_box.Secretbox.box_open key encrypted_sk nonce with
| None -> return_none
| Some bytes ->
match Data_encoding.Binary.of_bytes Signature.Secret_key.encoding bytes with
| None -> failwith "Corrupted wallet, deciphered key is invalid"
| Some sk -> return_some sk
let msg =
match (sk : Signature.secret_key) with
| Ed25519 sk ->
Data_encoding.Binary.to_bytes_exn Ed25519.Secret_key.encoding sk
| Secp256k1 sk ->
Data_encoding.Binary.to_bytes_exn Secp256k1.Secret_key.encoding sk
| P256 sk ->
Data_encoding.Binary.to_bytes_exn P256.Secret_key.encoding sk in
MBytes.concat "" [ salt ;
Crypto_box.Secretbox.box key msg nonce ]
let decrypt algo ~password ~encrypted_sk =
let salt = MBytes.sub encrypted_sk 0 salt_len in
let encrypted_sk =
MBytes.sub encrypted_sk salt_len encrypted_size in
let key = Crypto_box.Secretbox.unsafe_of_bytes (pbkdf ~salt ~password) in
match Crypto_box.Secretbox.box_open key encrypted_sk nonce, algo with
| None, _ -> return_none
| Some bytes, Signature.Ed25519 -> begin
match Data_encoding.Binary.of_bytes Ed25519.Secret_key.encoding bytes with
| Some sk -> return_some (Ed25519 sk : Signature.Secret_key.t)
| None -> failwith "Corrupted wallet, deciphered key is not a \
valid Ed25519 secret key"
end
| Some bytes, Signature.Secp256k1 -> begin
match Data_encoding.Binary.of_bytes Secp256k1.Secret_key.encoding bytes with
| Some sk -> return_some (Secp256k1 sk : Signature.Secret_key.t)
| None -> failwith "Corrupted wallet, deciphered key is not a \
valid Secp256k1 secret key"
end
| Some bytes, Signature.P256 -> begin
match Data_encoding.Binary.of_bytes P256.Secret_key.encoding bytes with
| Some sk -> return_some (P256 sk : Signature.Secret_key.t)
| None -> failwith "Corrupted wallet, deciphered key is not a \
valid P256 secret key"
end
end
module Encodings = struct
let ed25519 =
let length =
Hacl.Sign.skbytes + Crypto_box.boxzerobytes + Raw.salt_len in
Base58.register_encoding
~prefix: Base58.Prefix.ed25519_encrypted_seed
~length
~to_raw: (fun sk -> MBytes.to_string sk)
~of_raw: (fun buf ->
if String.length buf <> length then None
else Some (MBytes.of_string buf))
~wrap: (fun sk -> Encrypted_ed25519 sk)
let secp256k1 =
let open Libsecp256k1.External in
let length =
Key.secret_bytes + Crypto_box.boxzerobytes +Raw.salt_len in
Base58.register_encoding
~prefix: Base58.Prefix.secp256k1_encrypted_secret_key
~length
~to_raw: (fun sk -> MBytes.to_string sk)
~of_raw: (fun buf ->
if String.length buf <> length then None
else Some (MBytes.of_string buf))
~wrap: (fun sk -> Encrypted_secp256k1 sk)
let p256 =
let length =
Uecc.(sk_size secp256r1) + Crypto_box.boxzerobytes + Raw.salt_len in
Base58.register_encoding
~prefix: Base58.Prefix.p256_encrypted_secret_key
~length
~to_raw: (fun sk -> MBytes.to_string sk)
~of_raw: (fun buf ->
if String.length buf <> length then None
else Some (MBytes.of_string buf))
~wrap: (fun sk -> Encrypted_p256 sk)
let () =
Base58.check_encoded_prefix ed25519 "edesk" 88 ;
Base58.check_encoded_prefix secp256k1 "spesk" 88 ;
Base58.check_encoded_prefix p256 "p2esk" 88
end
let decrypted = Hashtbl.create 13
@ -51,9 +123,8 @@ let passwords = ref []
let rec interactive_decrypt_loop
(cctxt : #Client_context.prompter)
?name ~encrypted_sk =
begin
match name with
?name ~encrypted_sk algo =
begin match name with
| None ->
cctxt#prompt_password
"Enter password for encrypted key: "
@ -61,30 +132,35 @@ let rec interactive_decrypt_loop
cctxt#prompt_password
"Enter password for encrypted key \"%s\": " name
end >>=? fun password ->
Raw.decrypt ~password ~encrypted_sk >>=? function
| None ->
interactive_decrypt_loop cctxt ?name ~encrypted_sk
Raw.decrypt algo ~password ~encrypted_sk >>=? function
| Some sk ->
passwords := password :: !passwords ;
return sk
| None ->
interactive_decrypt_loop cctxt ?name ~encrypted_sk algo
let rec noninteractice_decrypt_loop ~encrypted_sk = function
let rec noninteractice_decrypt_loop algo ~encrypted_sk = function
| [] -> return_none
| password :: passwords ->
Raw.decrypt ~password ~encrypted_sk >>=? function
| None -> noninteractice_decrypt_loop ~encrypted_sk passwords
Raw.decrypt algo ~password ~encrypted_sk >>=? function
| None -> noninteractice_decrypt_loop algo ~encrypted_sk passwords
| Some sk -> return_some sk
let decrypt_payload cctxt ?name encrypted_sk =
match Base58.safe_decode encrypted_sk with
| None -> failwith "Not a Base58 encoded encrypted key"
| Some encrypted_sk ->
let encrypted_sk = MBytes.of_string encrypted_sk in
noninteractice_decrypt_loop ~encrypted_sk !passwords >>=? function
| Some sk -> return sk
| None -> interactive_decrypt_loop cctxt ?name ~encrypted_sk
begin match Base58.decode encrypted_sk with
| Some (Encrypted_ed25519 encrypted_sk) ->
return (Signature.Ed25519, encrypted_sk)
| Some (Encrypted_secp256k1 encrypted_sk) ->
return (Signature.Secp256k1, encrypted_sk)
| Some (Encrypted_p256 encrypted_sk) ->
return (Signature.P256, encrypted_sk)
| _ -> failwith "Not a Base58Check-encoded encrypted key"
end >>=? fun (algo, encrypted_sk) ->
noninteractice_decrypt_loop algo ~encrypted_sk !passwords >>=? function
| Some sk -> return sk
| None -> interactive_decrypt_loop cctxt ?name ~encrypted_sk algo
let decrypt cctxt ?name sk_uri =
let decrypt (cctxt : #Client_context.prompter) ?name sk_uri =
let payload = Uri.path (sk_uri : sk_uri :> Uri.t) in
decrypt_payload cctxt ?name payload >>=? fun sk ->
Hashtbl.replace decrypted sk_uri sk ;
@ -114,7 +190,11 @@ let rec read_passphrase (cctxt : #Client_context.io) =
let encrypt cctxt sk =
read_passphrase cctxt >>=? fun password ->
let payload = Raw.encrypt ~password sk in
let path = Base58.safe_encode (MBytes.to_string payload) in
let encoding = match sk with
| Ed25519 _ -> Encodings.ed25519
| Secp256k1 _ -> Encodings.secp256k1
| P256 _ -> Encodings.p256 in
let path = Base58.simple_encode encoding payload in
let sk_uri = Client_keys.make_sk_uri (Uri.make ~scheme ~path ()) in
Hashtbl.replace decrypted sk_uri sk ;
return sk_uri

View File

@ -10,7 +10,7 @@
module Make(C : sig val cctxt: Client_context.prompter end) : Client_keys.SIGNER
val decrypt:
#Client_context.io_wallet ->
#Client_context.prompter ->
?name:string ->
Client_keys.sk_uri -> Signature.secret_key tzresult Lwt.t