Signer/Encrypted: use custom Base58Check prefixes
This commit is contained in:
parent
dee9c470fd
commit
e92e1aee17
@ -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) *)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user