P2p: make nonces unpredictable when connecting

Avoid replay-attacks by preventing a node from determining one of the
nonces used in the encryption of a channel between two nodes.
This commit is contained in:
Fabrice Le Fessant 2018-05-18 16:57:43 +02:00 committed by Grégoire Henry
parent dcf27f48d9
commit 8986640a98
5 changed files with 57 additions and 15 deletions

View File

@ -64,6 +64,23 @@ let random_keypair () =
let zero_nonce = MBytes.make Nonce.bytes '\x00'
let random_nonce = Nonce.gen
let increment_nonce = Nonce.increment
let generate_nonce mbytes =
let hash = Blake2B.hash_bytes mbytes in
Nonce.of_bytes_exn @@ (Bigstring.sub (Blake2B.to_bytes hash) 0 Nonce.bytes)
let init_to_resp_seed = MBytes.of_string "Init -> Resp"
let resp_to_init_seed = MBytes.of_string "Resp -> Init"
let generate_nonces ~incoming ~sent_msg ~recv_msg =
let (init_msg, resp_msg, false)
| (resp_msg, init_msg, true) = (sent_msg, recv_msg, incoming) in
let nonce_init_to_resp =
generate_nonce [ init_msg ; resp_msg ; init_to_resp_seed ] in
let nonce_resp_to_init =
generate_nonce [ init_msg ; resp_msg ; resp_to_init_seed ] in
if incoming then
(nonce_init_to_resp, nonce_resp_to_init)
else
(nonce_resp_to_init, nonce_init_to_resp)
let precompute sk pk = Box.dh pk sk

View File

@ -16,6 +16,16 @@ val zero_nonce : nonce
val random_nonce : unit -> nonce
val increment_nonce : ?step:int -> nonce -> nonce
(** [generate_nonces ~incoming ~sent_msg ~recv_msg] generates two
nonces by hashing (Blake2B) the arguments. The nonces should be
used to initialize the encryption on the communication
channels. Because an attacker cannot control both messages,
it cannot determine the nonces that will be used to encrypt
the messages. The sent message should contains a random nonce,
and we should never send the exact same message twice. *)
val generate_nonces :
incoming:bool -> sent_msg:MBytes.t -> recv_msg:MBytes.t -> nonce * nonce
module Secretbox : sig
type key

View File

@ -125,23 +125,28 @@ module Connection_message = struct
| Some last ->
fail_unless (last = len) P2p_errors.Encoding_error >>=? fun () ->
MBytes.set_int16 buf 0 encoded_message_len ;
P2p_io_scheduler.write fd buf
P2p_io_scheduler.write fd buf >>=? fun () ->
(* We return the raw message as it is used later to compute
the nonces *)
return buf
let read fd =
let header_buf = MBytes.create Crypto.header_length in
P2p_io_scheduler.read_full
~len:Crypto.header_length fd header_buf >>=? fun () ->
let len = MBytes.get_uint16 header_buf 0 in
let buf = MBytes.create len in
P2p_io_scheduler.read_full ~len fd buf >>=? fun () ->
match Data_encoding.Binary.read encoding buf 0 len with
let pos = Crypto.header_length in
let buf = MBytes.create (pos + len) in
MBytes.set_int16 buf 0 len ;
P2p_io_scheduler.read_full ~len ~pos fd buf >>=? fun () ->
match Data_encoding.Binary.read encoding buf pos len with
| None ->
fail P2p_errors.Decoding_error
| Some (read_len, message) ->
if read_len <> len then
| Some (next_pos, message) ->
if next_pos <> pos+len then
fail P2p_errors.Decoding_error
else
return message
return (message, buf)
end
@ -176,15 +181,15 @@ let authenticate
~proof_of_work_target
~incoming fd (remote_addr, remote_socket_port as point)
?listening_port identity supported_versions =
let local_nonce = Crypto_box.random_nonce () in
let local_nonce_seed = Crypto_box.random_nonce () in
lwt_debug "Sending authenfication to %a" P2p_point.Id.pp point >>= fun () ->
Connection_message.write fd
{ public_key = identity.P2p_identity.public_key ;
proof_of_work_stamp = identity.proof_of_work_stamp ;
message_nonce = local_nonce ;
message_nonce = local_nonce_seed ;
port = listening_port ;
versions = supported_versions } >>=? fun () ->
Connection_message.read fd >>=? fun msg ->
versions = supported_versions } >>=? fun sent_msg ->
Connection_message.read fd >>=? fun (msg, recv_msg) ->
let remote_listening_port =
if incoming then msg.port else Some remote_socket_port in
let id_point = remote_addr, remote_listening_port in
@ -198,13 +203,13 @@ let authenticate
(P2p_errors.Not_enough_proof_of_work remote_peer_id) >>=? fun () ->
let channel_key =
Crypto_box.precompute identity.P2p_identity.secret_key msg.public_key in
let (local_nonce, remote_nonce) =
Crypto_box.generate_nonces ~incoming ~sent_msg ~recv_msg in
let cryptobox_data = { Crypto.channel_key ; local_nonce ; remote_nonce } in
let info =
{ P2p_connection.Info.peer_id = remote_peer_id ;
versions = msg.versions ; incoming ;
id_point ; remote_socket_port ;} in
let cryptobox_data =
{ Crypto.channel_key ; local_nonce ;
remote_nonce = msg.message_nonce } in
return (info, (fd, info, cryptobox_data))
type connection = {
@ -552,4 +557,3 @@ let close ?(wait = false) st =
Writer.shutdown st.writer >>= fun () ->
P2p_io_scheduler.close st.conn.fd >>= fun _ ->
Lwt.return_unit

View File

@ -166,6 +166,15 @@ module Nonce = struct
Bigstring.blit nonce 0 new_nonce 0 24 ;
incr_byte new_nonce step 22 ;
new_nonce
let of_bytes buf =
if Bigstring.length buf <> bytes then None else Some buf
let of_bytes_exn buf =
match of_bytes buf with
| Some s -> s
| None -> invalid_arg "Hacl.Nonce.of_bytes_exn: invalid length"
end
module Secretbox = struct

View File

@ -71,6 +71,8 @@ module Nonce : sig
val bytes : int
val gen : unit -> t
val increment : ?step:int -> t -> t
val of_bytes : Bigstring.t -> t option
val of_bytes_exn : Bigstring.t -> t
end
module Secretbox : sig