package spoke

  1. Overview
  2. Docs

The Flow implementation of SPAKE2+EE.

This module provides a concrete implementation of the handshake on the client side and the server side which is agnostic to any protocols. These implementations emits a t which supervises the user knows that when to read (Rd) and when to write (Wr).

From these implementations, the module provides a Mirage_flow.S implementation which requires a Mirage_flow.S implementation as the underlying implementation to read/write through a network.

The agnostic implementation of the handshake.

As a server, for instance, you have 2 available syscalls, one to read read : fd -> bytes -> int -> int -> int and one to write write : fd -> string -> unit. You receive a connection and you have an fd. You are able to compute the handshake:

let cfg = Flow.Cfg (Spoke.Pbkdf2, 16)
let identities = "Bob", "Alice"
let password = "Your Password"

let handle_client fd =
  let ctx = Flow.ctx () in
  let rec go = function
    | Rd { buf; off; len; k; } ->
      ( match read fd buf off len with
      | 0 -> go (k `End)
      | len -> go (k (`Len len)) )
    | Wr { str; off; len; k; } ->
      let str = String.sub str off len in
      write fd str ; go (k len)
    | Done (ciphers, sk) -> Ok (ciphers, sk)
    | Fail err -> Error err in
  go (Flow.handshake_server ctx ~password ~identity:identities cfg)

A ctx is required to keep incoming/outcoming data along the computation of t.

A Mirage_flow.S which handles ciphers.

Obviously, we can go further than just complete the handshake. We can finally start a communication with our peer through a symmetric cipher. The functor Make gives you the ability to upgrade a given flow implementation to a secured transmission protocol through a symmetric cipher from a shared weak password. The example below is about a server which handles client connections and it wants to upgrade them through symmetric ciphers to finally send a secured "Hello World".

module SPOKEFlow = Flow.Make (Tcpip)

let cfg = Flow.Cfg (Spoke.Pbkdf2, 16)
let identities = "Bob", "Alice"
let password = "Your Password"

let handle_client_with_with_secured_connection
  : SPOKEFlow.flow -> unit Lwt.t
  = fun flow ->
    SPOKEFlow.write flow "Secured Hello World!" >>= fun () ->
    ...

let handle_client (fd : Tcpip.flow) =
  SPOKEFlow.server_of_flow ~cfg ~password ~identity:identities
  >>= function
  | Ok flow -> handle_client_with_secured_connection flow
  | Error err -> ...
type ctx

Type of a context.

val ctx : unit -> ctx

ctx () creates a fresh ctx.

val remaining_bytes_of_ctx : ctx -> string option

remaining_bytes_of_ctx ctx returns bytes which are not consumed by the handshake but they are already consumed by the read syscall. In other words, at the end of the handshake, you may read more than you needed to and this function allows you to recover the excess.

type error = [
  1. | `Not_enough_space
  2. | `End_of_input
  3. | `Spoke of Spoke.error
]

The type of errors.

val pp_error : error Fmt.t

The pretty-printer of error.

type 'a t =
  1. | Rd of {
    1. buf : bytes;
    2. off : int;
    3. len : int;
    4. k : 'a krd;
    }
  2. | Wr of {
    1. str : string;
    2. off : int;
    3. len : int;
    4. k : 'a kwr;
    }
  3. | Done of 'a
  4. | Fail of error

The type of actions needed to compute the handshake.

and 'a krd = [ `End | `Len of int ] -> 'a t
and 'a kwr = int -> 'a t
type cfg =
  1. | Cfg : 'a Spoke.algorithm * 'a -> cfg

The type of configurations.

val handshake_client : ctx -> ?g:Stdlib.Random.State.t -> identity:(string * string) -> string -> ((Spoke.cipher * Spoke.cipher) * Spoke.shared_keys) t

handshake_client ctx ~identity password returns a t which leads users when they need to read or write. If the handshake succeed, we return Spoke.ciphers and Spoke.shared_keys. Otherwise, we return an error.

val handshake_server : ctx -> ?g:Stdlib.Random.State.t -> password:string -> identity:(string * string) -> cfg -> ((Spoke.cipher * Spoke.cipher) * Spoke.shared_keys) t

handshake_server ctx ~password ~identity cfg returns a t which leads users when they need to read or write. If the handshake succeed, we return Spoke.ciphers and Spoke.shared_keys. Otherwise, we return an error.

module Make (Flow : Mirage_flow.S) : sig ... end