From fd5c8702fe740b73e6cf3cba3160819928170a6b Mon Sep 17 00:00:00 2001
From: librelois <elois@ifee.fr>
Date: Thu, 29 Aug 2019 19:06:26 +0200
Subject: [PATCH] [feat] tools: impl PKSTL (Public Key Secure Transport Layer)

---
 Cargo.lock                                    | 111 ++++
 Cargo.toml                                    |   1 +
 lib/crypto/src/agreement.rs                   | 125 ----
 lib/crypto/src/errors.rs                      |  15 +-
 lib/crypto/src/lib.rs                         |   2 -
 lib/tools/pkstl/Cargo.toml                    |  36 ++
 lib/tools/pkstl/src/agreement.rs              | 211 ++++++
 lib/tools/pkstl/src/complete.rs               | 268 ++++++++
 lib/tools/pkstl/src/complete/message.rs       |  39 ++
 lib/tools/pkstl/src/complete/serde.rs         |  63 ++
 .../pkstl/src/complete/serde/deserializer.rs  | 106 +++
 .../pkstl/src/complete/serde/serializer.rs    | 121 ++++
 lib/tools/pkstl/src/complete/writer.rs        | 102 +++
 lib/tools/pkstl/src/config.rs                 |  78 +++
 lib/tools/pkstl/src/constants.rs              |  49 ++
 lib/tools/pkstl/src/digest.rs                 |  20 +
 lib/tools/pkstl/src/encryption.rs             | 158 +++++
 .../src/encryption/chacha20_poly1305_aead.rs} |  91 ++-
 lib/tools/pkstl/src/errors.rs                 | 106 +++
 lib/tools/pkstl/src/format.rs                 | 156 +++++
 lib/tools/pkstl/src/lib.rs                    | 100 +++
 lib/tools/pkstl/src/message.rs                | 494 ++++++++++++++
 lib/tools/pkstl/src/minimal.rs                | 604 ++++++++++++++++++
 lib/tools/pkstl/src/reader.rs                 | 373 +++++++++++
 lib/tools/pkstl/src/seeds.rs                  | 151 +++++
 lib/tools/pkstl/src/signature.rs              |  30 +
 lib/tools/pkstl/src/status.rs                 | 127 ++++
 lib/tools/pkstl/tests/complete_mode.rs        | 196 ++++++
 lib/tools/pkstl/tests/complete_serde_mode.rs  | 236 +++++++
 lib/tools/pkstl/tests/minimal_mode.rs         | 472 ++++++++++++++
 30 files changed, 4451 insertions(+), 190 deletions(-)
 delete mode 100644 lib/crypto/src/agreement.rs
 create mode 100644 lib/tools/pkstl/Cargo.toml
 create mode 100644 lib/tools/pkstl/src/agreement.rs
 create mode 100644 lib/tools/pkstl/src/complete.rs
 create mode 100644 lib/tools/pkstl/src/complete/message.rs
 create mode 100644 lib/tools/pkstl/src/complete/serde.rs
 create mode 100644 lib/tools/pkstl/src/complete/serde/deserializer.rs
 create mode 100644 lib/tools/pkstl/src/complete/serde/serializer.rs
 create mode 100644 lib/tools/pkstl/src/complete/writer.rs
 create mode 100644 lib/tools/pkstl/src/config.rs
 create mode 100644 lib/tools/pkstl/src/constants.rs
 create mode 100644 lib/tools/pkstl/src/digest.rs
 create mode 100644 lib/tools/pkstl/src/encryption.rs
 rename lib/{crypto/src/encryption.rs => tools/pkstl/src/encryption/chacha20_poly1305_aead.rs} (53%)
 create mode 100644 lib/tools/pkstl/src/errors.rs
 create mode 100644 lib/tools/pkstl/src/format.rs
 create mode 100644 lib/tools/pkstl/src/lib.rs
 create mode 100644 lib/tools/pkstl/src/message.rs
 create mode 100644 lib/tools/pkstl/src/minimal.rs
 create mode 100644 lib/tools/pkstl/src/reader.rs
 create mode 100644 lib/tools/pkstl/src/seeds.rs
 create mode 100644 lib/tools/pkstl/src/signature.rs
 create mode 100644 lib/tools/pkstl/src/status.rs
 create mode 100644 lib/tools/pkstl/tests/complete_mode.rs
 create mode 100644 lib/tools/pkstl/tests/complete_serde_mode.rs
 create mode 100644 lib/tools/pkstl/tests/minimal_mode.rs

diff --git a/Cargo.lock b/Cargo.lock
index 8e35fc86..baee1280 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,10 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+[[package]]
+name = "adler32"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "aho-corasick"
 version = "0.7.6"
@@ -227,6 +232,14 @@ name = "constant_time_eq"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "crc32fast"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "crossbeam-deque"
 version = "0.2.0"
@@ -326,6 +339,15 @@ dependencies = [
  "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "ctor"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "quote 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "difference"
 version = "2.0.0"
@@ -872,6 +894,17 @@ name = "fake-simd"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "flate2"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.6"
@@ -917,6 +950,11 @@ dependencies = [
  "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "half"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "heck"
 version = "0.3.1"
@@ -1092,6 +1130,23 @@ dependencies = [
  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "miniz-sys"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "mio"
 version = "0.6.19"
@@ -1223,6 +1278,14 @@ dependencies = [
  "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "output_vt100"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "pbkdf2"
 version = "0.3.0"
@@ -1293,6 +1356,23 @@ name = "pkg-config"
 version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "pkstl"
+version = "0.1.0"
+dependencies = [
+ "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chacha20-poly1305-aead 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ring 0.16.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_cbor 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "pretty_assertions"
 version = "0.5.1"
@@ -1302,6 +1382,17 @@ dependencies = [
  "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "pretty_assertions"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "prettytable-rs"
 version = "0.8.0"
@@ -1683,6 +1774,16 @@ dependencies = [
  "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "serde_cbor"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "serde_derive"
 version = "1.0.99"
@@ -2172,6 +2273,7 @@ dependencies = [
 ]
 
 [metadata]
+"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
 "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
 "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
@@ -2201,6 +2303,7 @@ dependencies = [
 "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
 "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
+"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
@@ -2211,6 +2314,7 @@ dependencies = [
 "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
 "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d"
 "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
+"checksum ctor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5b6b2f4752cc29efbfd03474c532ce8f916f2d44ec5bb8c21f93bc76e5365528"
 "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
 "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
@@ -2219,6 +2323,7 @@ dependencies = [
 "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
 "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
 "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+"checksum flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2adaffba6388640136149e18ed080b77a78611c1e1d6de75aedcdf78df5d4682"
 "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
 "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
@@ -2226,6 +2331,7 @@ dependencies = [
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+"checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836"
 "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
 "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
 "checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
@@ -2248,6 +2354,8 @@ dependencies = [
 "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
 "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
 "checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
+"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202"
+"checksum miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7108aff85b876d06f22503dcce091e29f76733b2bfdd91eebce81f5e68203a10"
 "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
 "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
@@ -2262,6 +2370,7 @@ dependencies = [
 "checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15"
 "checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884"
 "checksum os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7edc011af0ae98b7f88cf7e4a83b70a54a75d2b8cb013d6efd02e5956207e9eb"
+"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
 "checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9"
 "checksum pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "deb73390ab68d81992bd994d145f697451bb0b54fd39738e72eef32458ad6907"
 "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
@@ -2271,6 +2380,7 @@ dependencies = [
 "checksum pest_meta 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f249ea6de7c7b7aba92b4ff4376a994c6dbd98fd2166c89d5c4947397ecb574d"
 "checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af"
 "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
+"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
 "checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
 "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
 "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
@@ -2314,6 +2424,7 @@ dependencies = [
 "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 "checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
+"checksum serde_cbor 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "318690c4f04ae6553665f3846c0614c9995bb1ea51a2f1c5c4b4ed338c248b49"
 "checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425"
 "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
 "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
diff --git a/Cargo.toml b/Cargo.toml
index 6885a622..8bce67f4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,6 +26,7 @@ members = [
     "lib/tests-tools/common-tests-tools",
     "lib/tools/common-tools",
     "lib/tools/json-pest-parser",
+    "lib/tools/pkstl",
     "lib/tools/rules-engine",
 ]
 
diff --git a/lib/crypto/src/agreement.rs b/lib/crypto/src/agreement.rs
deleted file mode 100644
index 6c6b8246..00000000
--- a/lib/crypto/src/agreement.rs
+++ /dev/null
@@ -1,125 +0,0 @@
-//  Copyright (C) 2017-2019  The AXIOM TEAM Association.
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-//! Manage cryptographic agreement operations.
-
-use crate::errors::CryptoError;
-use ring::{agreement, digest, pbkdf2, rand};
-use std::num::NonZeroU32;
-
-const SHARED_SECRET_LEN: usize = digest::SHA384_OUTPUT_LEN;
-const ITERATIONS: u32 = 3;
-
-static PBKDF2_ALG: pbkdf2::Algorithm = pbkdf2::PBKDF2_HMAC_SHA384;
-
-#[derive(Clone)]
-/// Ephemeral public key used once to generate shared secret
-pub struct EphemeralPublicKey(agreement::PublicKey);
-
-impl AsRef<[u8]> for EphemeralPublicKey {
-    fn as_ref(&self) -> &[u8] {
-        self.0.as_ref()
-    }
-}
-
-/// Ephemeral key pair used once to generate shared secret
-pub struct EphemeralKeyPair {
-    privkey: agreement::EphemeralPrivateKey,
-    pubkey: EphemeralPublicKey,
-}
-
-impl EphemeralKeyPair {
-    /// Generate ephemeral key pair
-    pub fn generate() -> Result<Self, CryptoError> {
-        let rng = rand::SystemRandom::new();
-        let privkey = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)
-            .map_err(|_| CryptoError::FailToGenEphemerKeyPair)?;
-        let pubkey = EphemeralPublicKey(
-            privkey
-                .compute_public_key()
-                .map_err(|_| CryptoError::FailToGenEphemerPubKey)?,
-        );
-
-        Ok(EphemeralKeyPair { privkey, pubkey })
-    }
-    /// Get ephemeral public key
-    pub fn public_key(&self) -> &EphemeralPublicKey {
-        &self.pubkey
-    }
-    /// Compute shared secret
-    pub fn compute_shared_secret(
-        self,
-        other_ephemeral_public_key: &EphemeralPublicKey,
-        server: bool,
-    ) -> Result<[u8; SHARED_SECRET_LEN], CryptoError> {
-        let salt = if server {
-            self.pubkey.as_ref()
-        } else {
-            other_ephemeral_public_key.as_ref()
-        };
-
-        agreement::agree_ephemeral(
-            self.privkey,
-            &agreement::UnparsedPublicKey::new(
-                &agreement::X25519,
-                other_ephemeral_public_key.as_ref(),
-            ),
-            CryptoError::FailToComputeAgreement,
-            |key_material| Ok(derive(key_material, salt)),
-        )
-    }
-}
-
-fn derive(seed: &[u8], salt: &[u8]) -> [u8; SHARED_SECRET_LEN] {
-    let mut store_credentials: [u8; SHARED_SECRET_LEN] = [0u8; SHARED_SECRET_LEN];
-    pbkdf2::derive(
-        PBKDF2_ALG,
-        NonZeroU32::new(ITERATIONS).expect("ITERATIONS must be > 0"),
-        salt,
-        seed,
-        &mut store_credentials,
-    );
-    store_credentials
-}
-
-#[cfg(test)]
-mod tests {
-
-    use super::*;
-
-    #[test]
-    fn test_exchange_dh() -> Result<(), CryptoError> {
-        let ephemeral_kp_server = EphemeralKeyPair::generate()?;
-        let ephemeral_kp_client = EphemeralKeyPair::generate()?;
-
-        let ephemeral_pk_server = ephemeral_kp_server.public_key().clone();
-        let ephemeral_pk_client = ephemeral_kp_client.public_key().clone();
-
-        let shared_secret_server =
-            ephemeral_kp_server.compute_shared_secret(&ephemeral_kp_client.public_key(), true)?;
-
-        let shared_secret_client =
-            ephemeral_kp_client.compute_shared_secret(&ephemeral_pk_server, false)?;
-
-        assert_eq!(shared_secret_server.to_vec(), shared_secret_client.to_vec());
-
-        println!("ephemeral_pk_server={:?}", ephemeral_pk_server.as_ref());
-        println!("ephemeral_pk_client={:?}", ephemeral_pk_client.as_ref());
-        println!("shared_secret_server={:?}", shared_secret_server.to_vec());
-        println!("shared_secret_client={:?}", shared_secret_client.to_vec());
-        //panic!();
-        Ok(())
-    }
-}
diff --git a/lib/crypto/src/errors.rs b/lib/crypto/src/errors.rs
index 9d4b43db..f2aa44ad 100644
--- a/lib/crypto/src/errors.rs
+++ b/lib/crypto/src/errors.rs
@@ -15,17 +15,6 @@
 
 //! Manage cryptographic errors.
 
-#[derive(Debug)]
+#[derive(Clone, Copy, Debug)]
 /// Cryptographic error
-pub enum CryptoError {
-    /// Fail to compute agreement
-    FailToComputeAgreement,
-    /// Fail to decrypt datas
-    FailToDecryptDatas(chacha20_poly1305_aead::DecryptError),
-    /// Fail to encrypt datas
-    FailToEncryptDatas(std::io::Error),
-    /// Fail to generate ephemeral key pair
-    FailToGenEphemerKeyPair,
-    /// Fail to generate ephemeral public key
-    FailToGenEphemerPubKey,
-}
+pub enum CryptoError {}
diff --git a/lib/crypto/src/lib.rs b/lib/crypto/src/lib.rs
index 9ce42463..5020ebcc 100644
--- a/lib/crypto/src/lib.rs
+++ b/lib/crypto/src/lib.rs
@@ -36,9 +36,7 @@ extern crate serde_derive;
 #[macro_use]
 extern crate log;
 
-pub mod agreement;
 pub mod bases;
-pub mod encryption;
 pub mod errors;
 pub mod hashs;
 pub mod keys;
diff --git a/lib/tools/pkstl/Cargo.toml b/lib/tools/pkstl/Cargo.toml
new file mode 100644
index 00000000..e489802d
--- /dev/null
+++ b/lib/tools/pkstl/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+name = "pkstl"
+version = "0.1.0"
+authors = ["elois <elois@duniter.org>"]
+description = "Public Key Secure Transport Layer."
+repository = "https://git.duniter.org/nodes/rust/duniter-rs"
+readme = "README.md"
+keywords = ["security", "transport", "cryptography"]
+license = "AGPL-3.0"
+edition = "2018"
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+bincode = { version = "1.0.*", optional = true }
+chacha20-poly1305-aead = "0.1.2"
+clear_on_drop = "0.2.3"
+failure = "0.1.5"
+flate2 = { version = "1.0.11", optional = true }
+ring = "0.16.5"
+serde = { version = "1.0.*", features = ["derive"], optional = true }
+serde_cbor = { version = "0.10.1", optional = true }
+serde_json = { version = "1.0.40", optional = true }
+log = "0.4.*"
+
+[dev-dependencies]
+pretty_assertions = "0.6.1"
+
+[features]
+default = ["zip-sign"]
+zip-sign = ["flate2"]
+ser = ["zip-sign", "serde"]
+bin = ["ser", "bincode"]
+cbor = ["ser", "serde_cbor"]
+json = ["ser", "serde_json"]
diff --git a/lib/tools/pkstl/src/agreement.rs b/lib/tools/pkstl/src/agreement.rs
new file mode 100644
index 00000000..3bc6f370
--- /dev/null
+++ b/lib/tools/pkstl/src/agreement.rs
@@ -0,0 +1,211 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage cryptographic agreement operations.
+
+use crate::seeds::{Seed32, Seed48, Seed64};
+use crate::{Error, Result};
+use ring::{agreement, pbkdf2, rand};
+use std::num::NonZeroU32;
+
+const ITERATIONS: u32 = 3;
+
+#[derive(Clone, Copy, Debug)]
+pub enum SharedSecretLen {
+    B32,
+    B48,
+    B64,
+}
+
+impl SharedSecretLen {
+    fn algo(self) -> pbkdf2::Algorithm {
+        match self {
+            Self::B32 => pbkdf2::PBKDF2_HMAC_SHA256,
+            Self::B48 => pbkdf2::PBKDF2_HMAC_SHA384,
+            Self::B64 => pbkdf2::PBKDF2_HMAC_SHA512,
+        }
+    }
+}
+
+pub enum SharedSecret {
+    B32(Seed32),
+    B48(Seed48),
+    B64(Seed64),
+}
+
+impl AsMut<[u8]> for SharedSecret {
+    fn as_mut(&mut self) -> &mut [u8] {
+        match self {
+            Self::B32(seed) => seed.as_mut(),
+            Self::B48(seed) => seed.as_mut(),
+            Self::B64(seed) => seed.as_mut(),
+        }
+    }
+}
+
+impl SharedSecret {
+    fn new(len: SharedSecretLen) -> Self {
+        match len {
+            SharedSecretLen::B32 => SharedSecret::B32(Seed32::default()),
+            SharedSecretLen::B48 => SharedSecret::B48(Seed48::default()),
+            SharedSecretLen::B64 => SharedSecret::B64(Seed64::default()),
+        }
+    }
+}
+
+#[derive(Clone)]
+/// Ephemeral public key used once to generate shared secret
+pub struct EphemeralPublicKey(agreement::PublicKey);
+
+impl AsRef<[u8]> for EphemeralPublicKey {
+    fn as_ref(&self) -> &[u8] {
+        self.0.as_ref()
+    }
+}
+
+/// Ephemeral key pair used once to generate shared secret
+pub struct EphemeralKeyPair {
+    privkey: agreement::EphemeralPrivateKey,
+    pubkey: EphemeralPublicKey,
+}
+
+impl EphemeralKeyPair {
+    /// Generate ephemeral key pair
+    pub fn generate() -> Result<Self> {
+        let rng = rand::SystemRandom::new();
+        let privkey = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)
+            .map_err(|_| Error::FailToGenEphemerKeyPair)?;
+        let pubkey = EphemeralPublicKey(
+            privkey
+                .compute_public_key()
+                .map_err(|_| Error::FailToGenEphemerPubKey)?,
+        );
+
+        Ok(EphemeralKeyPair { privkey, pubkey })
+    }
+    /// Get ephemeral public key
+    pub fn public_key(&self) -> &EphemeralPublicKey {
+        &self.pubkey
+    }
+    /// Compute shared secret
+    pub fn compute_shared_secret(
+        self,
+        other_ephemeral_public_key: &[u8],
+        shared_secret_len: SharedSecretLen,
+    ) -> Result<SharedSecret> {
+        let salt = if self.pubkey.as_ref() > other_ephemeral_public_key {
+            self.pubkey.as_ref()
+        } else {
+            other_ephemeral_public_key
+        };
+
+        agreement::agree_ephemeral(
+            self.privkey,
+            &agreement::UnparsedPublicKey::new(&agreement::X25519, other_ephemeral_public_key),
+            Error::FailToComputeAgreement,
+            |key_material| Ok(derive(key_material, salt, shared_secret_len)),
+        )
+    }
+}
+
+fn derive(seed: &[u8], salt: &[u8], shared_secret_len: SharedSecretLen) -> SharedSecret {
+    let mut shared_secret = SharedSecret::new(shared_secret_len);
+    pbkdf2::derive(
+        shared_secret_len.algo(),
+        NonZeroU32::new(ITERATIONS).expect("ITERATIONS must be > 0"),
+        salt,
+        seed,
+        shared_secret.as_mut(),
+    );
+    shared_secret
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    #[test]
+    fn test_exchange_dh_shared_secret_48b() -> Result<()> {
+        let ephemeral_kp_server = EphemeralKeyPair::generate()?;
+        let ephemeral_kp_client = EphemeralKeyPair::generate()?;
+
+        let ephemeral_pk_server = ephemeral_kp_server.public_key().clone();
+        let ephemeral_pk_client = ephemeral_kp_client.public_key().clone();
+
+        // Sharer secret of 48 bytes
+        let mut shared_secret_server_48b = ephemeral_kp_server.compute_shared_secret(
+            ephemeral_kp_client.public_key().as_ref(),
+            SharedSecretLen::B48,
+        )?;
+
+        let mut shared_secret_client_48b = ephemeral_kp_client
+            .compute_shared_secret(ephemeral_pk_server.as_ref(), SharedSecretLen::B48)?;
+
+        assert_eq!(
+            shared_secret_server_48b.as_mut().to_vec(),
+            shared_secret_client_48b.as_mut().to_vec()
+        );
+
+        println!("ephemeral_pk_server={:?}", ephemeral_pk_server.as_ref());
+        println!("ephemeral_pk_client={:?}", ephemeral_pk_client.as_ref());
+        println!(
+            "shared_secret_server={:?}",
+            shared_secret_server_48b.as_mut()
+        );
+        println!(
+            "shared_secret_client={:?}",
+            shared_secret_client_48b.as_mut()
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_exchange_dh_shared_secret_64b() -> Result<()> {
+        let ephemeral_kp_server = EphemeralKeyPair::generate()?;
+        let ephemeral_kp_client = EphemeralKeyPair::generate()?;
+
+        let ephemeral_pk_server = ephemeral_kp_server.public_key().clone();
+        let ephemeral_pk_client = ephemeral_kp_client.public_key().clone();
+
+        // Sharer secret of 64 bytes
+        let mut shared_secret_server_64b = ephemeral_kp_server.compute_shared_secret(
+            ephemeral_kp_client.public_key().as_ref(),
+            SharedSecretLen::B64,
+        )?;
+
+        let mut shared_secret_client_64b = ephemeral_kp_client
+            .compute_shared_secret(ephemeral_pk_server.as_ref(), SharedSecretLen::B64)?;
+
+        assert_eq!(
+            shared_secret_server_64b.as_mut().to_vec(),
+            shared_secret_client_64b.as_mut().to_vec()
+        );
+
+        println!("ephemeral_pk_server={:?}", ephemeral_pk_server.as_ref());
+        println!("ephemeral_pk_client={:?}", ephemeral_pk_client.as_ref());
+        println!(
+            "shared_secret_server={:?}",
+            shared_secret_server_64b.as_mut()
+        );
+        println!(
+            "shared_secret_client={:?}",
+            shared_secret_client_64b.as_mut()
+        );
+
+        Ok(())
+    }
+}
diff --git a/lib/tools/pkstl/src/complete.rs b/lib/tools/pkstl/src/complete.rs
new file mode 100644
index 00000000..dc04d7b5
--- /dev/null
+++ b/lib/tools/pkstl/src/complete.rs
@@ -0,0 +1,268 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage complete secure and decentralized transport layer.
+
+pub mod message;
+#[cfg(feature = "ser")]
+pub mod serde;
+pub mod writer;
+
+#[cfg(feature = "ser")]
+pub use self::serde::IncomingMessage;
+
+use crate::{Error, Message, MinimalSecureLayer, Result, SdtlConfig, Seed32};
+use flate2::write::{DeflateDecoder, DeflateEncoder};
+use message::IncomingBinaryMessage;
+use ring::signature::Ed25519KeyPair;
+use std::io::{BufWriter, Write};
+
+#[cfg(feature = "ser")]
+use ::serde::de::DeserializeOwned;
+#[cfg(feature = "ser")]
+use ::serde::Serialize;
+#[cfg(feature = "ser")]
+use std::fmt::Debug;
+
+/// Secure layer
+pub struct SecureLayer {
+    config: SdtlConfig,
+    minimal_secure_layer: MinimalSecureLayer,
+    sig_key_pair: Ed25519KeyPair,
+}
+
+impl SecureLayer {
+    /// Change configuration
+    #[inline]
+    pub fn change_config(&mut self, new_config: SdtlConfig) {
+        self.config = new_config;
+        self.minimal_secure_layer.change_config(new_config.minimal);
+    }
+    fn compress(&self, bin_message: &[u8]) -> Result<Vec<u8>> {
+        // Create buffer
+        let buffer = BufWriter::new(Vec::with_capacity(bin_message.len()));
+
+        // Determine compression level
+        let compression_level = if bin_message.len() < self.config.compression_min_size {
+            flate2::Compression::none()
+        } else {
+            self.config.compression
+        };
+
+        // Create compressor
+        let mut deflate_encoder = DeflateEncoder::new(buffer, compression_level);
+
+        // Write message in compressor buffer
+        deflate_encoder
+            .write_all(&bin_message[..])
+            .map_err(Error::ZipError)?;
+
+        // Finalize compression
+        let bin_msg_compressed: BufWriter<Vec<u8>> =
+            deflate_encoder.finish().map_err(Error::ZipError)?;
+
+        // Flush buffer
+        let bin_msg_compressed = bin_msg_compressed
+            .into_inner()
+            .map_err(|_| Error::BufferFlushError)?;
+
+        Ok(bin_msg_compressed)
+    }
+    /// Create secure layer
+    #[inline]
+    pub fn create(
+        config: SdtlConfig,
+        sig_key_pair_seed: Option<Seed32>,
+        expected_remote_sig_pubkey: Option<Vec<u8>>,
+    ) -> Result<Self> {
+        let seed = sig_key_pair_seed.unwrap_or_else(Seed32::random);
+
+        let secure_layer = SecureLayer {
+            config,
+            minimal_secure_layer: MinimalSecureLayer::create(
+                config.minimal,
+                expected_remote_sig_pubkey,
+            )?,
+            sig_key_pair: Ed25519KeyPair::from_seed_unchecked(seed.as_ref())
+                .map_err(|_| Error::FailtoGenSigKeyPair)?,
+        };
+
+        Ok(secure_layer)
+    }
+    /// Read binary incoming datas
+    pub fn read_bin(&mut self, incoming_datas: &[u8]) -> Result<Option<IncomingBinaryMessage>> {
+        let message_opt = self.minimal_secure_layer.read(incoming_datas)?;
+
+        if let Some(message) = message_opt {
+            let user_message = match message {
+                Message::Connect {
+                    custom_datas,
+                    sig_pubkey,
+                    ..
+                } => IncomingBinaryMessage::Connect {
+                    custom_datas: if let Some(custom_datas) = custom_datas {
+                        Some(Self::uncompress(&custom_datas)?)
+                    } else {
+                        None
+                    },
+                    peer_sig_public_key: sig_pubkey,
+                },
+                Message::Ack { custom_datas } => IncomingBinaryMessage::Ack {
+                    custom_datas: if let Some(custom_datas) = custom_datas {
+                        Some(Self::uncompress(&custom_datas)?)
+                    } else {
+                        None
+                    },
+                },
+                Message::Message { custom_datas } => IncomingBinaryMessage::Message {
+                    datas: if let Some(custom_datas) = custom_datas {
+                        Some(Self::uncompress(&custom_datas)?)
+                    } else {
+                        None
+                    },
+                },
+            };
+            Ok(Some(user_message))
+        } else {
+            Ok(None)
+        }
+    }
+    /// Read incoming datas
+    #[cfg(feature = "ser")]
+    #[inline]
+    pub fn read<M>(&mut self, incoming_datas: &[u8]) -> Result<Option<IncomingMessage<M>>>
+    where
+        M: Debug + DeserializeOwned,
+    {
+        self::serde::deserializer::read::<M>(self, incoming_datas)
+    }
+    fn uncompress(bin_zip_msg: &[u8]) -> Result<Vec<u8>> {
+        let mut deflate_decoder = DeflateDecoder::new(Vec::with_capacity(bin_zip_msg.len() * 5));
+        deflate_decoder
+            .write_all(&bin_zip_msg)
+            .map_err(Error::ZipError)?;
+        deflate_decoder.finish().map_err(Error::ZipError)
+    }
+    /// Write ack message with optional binary custom datas
+    pub fn write_ack_msg_bin<W>(
+        &mut self,
+        custom_datas: Option<&[u8]>,
+        writer: &mut BufWriter<W>,
+    ) -> Result<()>
+    where
+        W: Write,
+    {
+        // Serialize and compress custom datas
+        let custom_datas = if let Some(custom_datas) = custom_datas {
+            Some(self.compress(custom_datas)?)
+        } else {
+            None
+        };
+
+        writer::write_ack_msg::<W>(self, custom_datas, writer)
+    }
+    /// Write ack message with optional custom datas
+    #[cfg(feature = "ser")]
+    #[inline]
+    pub fn write_ack_msg<M, W>(
+        &mut self,
+        custom_datas: Option<&M>,
+        writer: &mut BufWriter<W>,
+    ) -> Result<()>
+    where
+        M: Serialize,
+        W: Write,
+    {
+        self::serde::serializer::write_ack_msg::<M, W>(self, custom_datas, writer)
+    }
+    /// Write connect message with optional binary custom datas
+    pub fn write_connect_msg_bin<W>(
+        &mut self,
+        custom_datas: Option<&[u8]>,
+        writer: &mut BufWriter<W>,
+    ) -> Result<()>
+    where
+        W: Write,
+    {
+        // Compress custom datas
+        let custom_datas = if let Some(custom_datas) = custom_datas {
+            Some(self.compress(custom_datas)?)
+        } else {
+            None
+        };
+
+        writer::write_connect_msg(self, custom_datas, writer)
+    }
+    /// Write connect message with optional custom datas
+    #[cfg(feature = "ser")]
+    #[inline]
+    pub fn write_connect_msg<M, W>(
+        &mut self,
+        custom_datas: Option<&M>,
+        writer: &mut BufWriter<W>,
+    ) -> Result<()>
+    where
+        M: Serialize,
+        W: Write,
+    {
+        self::serde::serializer::write_connect_msg::<M, W>(self, custom_datas, writer)
+    }
+    /*/// Split secure layer in writer and reader
+    pub fn split(self) -> Result<(SecureWriter, SecureReader)> {
+        unimplemented!()
+    }*/
+    /// Write message on a writer
+    #[cfg(feature = "ser")]
+    #[inline]
+    pub fn write<M, W>(&mut self, message: &M, writer: &mut BufWriter<W>) -> Result<()>
+    where
+        M: Serialize,
+        W: Write,
+    {
+        self::serde::serializer::write_message::<M, W>(self, message, writer)
+    }
+    /// Write binary message on a writer
+    pub fn write_bin<W>(&mut self, binary_message: &[u8], writer: &mut BufWriter<W>) -> Result<()>
+    where
+        W: Write,
+    {
+        // Compress message
+        let bin_zip_msg = self.compress(&binary_message[..])?;
+
+        writer::write_bin_message::<W>(self, &bin_zip_msg, writer)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    #[cfg(feature = "ser")]
+    use crate::MessageFormat;
+    use crate::SdtlMinimalConfig;
+
+    #[test]
+    fn test_change_config() -> Result<()> {
+        let mut msl = SecureLayer::create(SdtlConfig::default(), None, None)?;
+        msl.change_config(SdtlConfig {
+            compression: flate2::Compression::fast(),
+            compression_min_size: 8_192,
+            #[cfg(feature = "ser")]
+            message_format: MessageFormat::RawBinary,
+            minimal: SdtlMinimalConfig::default(),
+        });
+        Ok(())
+    }
+}
diff --git a/lib/tools/pkstl/src/complete/message.rs b/lib/tools/pkstl/src/complete/message.rs
new file mode 100644
index 00000000..c14295e8
--- /dev/null
+++ b/lib/tools/pkstl/src/complete/message.rs
@@ -0,0 +1,39 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage complete Public Key Secure Transport Layer.
+//! Sub-module define incoming messages format.
+
+/// Incoming binary Message
+#[derive(Debug)]
+pub enum IncomingBinaryMessage {
+    /// Connect message
+    Connect {
+        /// Your custom datas
+        custom_datas: Option<Vec<u8>>,
+        /// Peer public key of signature algorithm
+        peer_sig_public_key: Vec<u8>,
+    },
+    /// Ack message
+    Ack {
+        /// Your custom datas
+        custom_datas: Option<Vec<u8>>,
+    },
+    /// Message
+    Message {
+        /// Message datas (This is an option because it's possible to receive an empty message)
+        datas: Option<Vec<u8>>,
+    },
+}
diff --git a/lib/tools/pkstl/src/complete/serde.rs b/lib/tools/pkstl/src/complete/serde.rs
new file mode 100644
index 00000000..d2723fda
--- /dev/null
+++ b/lib/tools/pkstl/src/complete/serde.rs
@@ -0,0 +1,63 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage complete secure and decentralized transport layer with serialization/deserialization.
+
+use serde::de::DeserializeOwned;
+use std::fmt::Debug;
+
+pub mod deserializer;
+pub mod serializer;
+
+const HEADER_FORMAT_LEN: usize = 4;
+
+/// Incoming Message
+#[derive(Debug)]
+pub enum IncomingMessage<M: Debug + DeserializeOwned> {
+    /// Connect message
+    Connect {
+        /// Your custom datas
+        custom_datas: Option<M>,
+        /// Peer public key of signature algorithm
+        peer_sig_public_key: Vec<u8>,
+    },
+    /// Ack message
+    Ack {
+        /// Your custom datas
+        custom_datas: Option<M>,
+    },
+    /// Message
+    Message {
+        /// Message datas (This is an option because it's possible to receive an empty message)
+        datas: Option<M>,
+    },
+}
+
+#[derive(Debug)]
+pub enum SerdeError {
+    #[cfg(feature = "bin")]
+    /// Bincode error
+    BincodeError(String),
+    #[cfg(feature = "cbor")]
+    /// Cbor error
+    CborError(serde_cbor::error::Error),
+    #[cfg(feature = "json")]
+    /// Json error
+    JsonError(serde_json::Error),
+    /// For the "raw binary" format, use the functions suffixed by _bin
+    UseSuffixedBinFunctions,
+    /// Not copyable error for linter
+    _IoError(std::io::Error),
+}
diff --git a/lib/tools/pkstl/src/complete/serde/deserializer.rs b/lib/tools/pkstl/src/complete/serde/deserializer.rs
new file mode 100644
index 00000000..520b3fda
--- /dev/null
+++ b/lib/tools/pkstl/src/complete/serde/deserializer.rs
@@ -0,0 +1,106 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Define PKSTL deserializer.
+
+use super::SerdeError;
+use super::{IncomingMessage, HEADER_FORMAT_LEN};
+use crate::format::MessageFormat;
+use crate::{Error, IncomingBinaryMessage, Result, SecureLayer};
+use serde::de::DeserializeOwned;
+use std::convert::TryFrom;
+use std::fmt::Debug;
+
+pub(crate) fn read<M>(
+    sl: &mut SecureLayer,
+    incoming_datas: &[u8],
+) -> Result<Option<IncomingMessage<M>>>
+where
+    M: Debug + DeserializeOwned,
+{
+    let bin_msg_opt = sl.read_bin(incoming_datas)?;
+
+    if let Some(bin_msg) = bin_msg_opt {
+        let user_message = match bin_msg {
+            IncomingBinaryMessage::Connect {
+                custom_datas,
+                peer_sig_public_key,
+            } => IncomingMessage::Connect {
+                custom_datas: if let Some(custom_datas) = custom_datas {
+                    Some(deserialize(&custom_datas)?)
+                } else {
+                    None
+                },
+                peer_sig_public_key,
+            },
+            IncomingBinaryMessage::Ack { custom_datas } => IncomingMessage::Ack {
+                custom_datas: if let Some(custom_datas) = custom_datas {
+                    Some(deserialize(&custom_datas)?)
+                } else {
+                    None
+                },
+            },
+            IncomingBinaryMessage::Message { datas } => IncomingMessage::Message {
+                datas: if let Some(datas) = datas {
+                    Some(deserialize(&datas)?)
+                } else {
+                    None
+                },
+            },
+        };
+        Ok(Some(user_message))
+    } else {
+        Ok(None)
+    }
+}
+
+#[inline]
+fn deserialize<M: Debug + DeserializeOwned>(binary_message: &[u8]) -> Result<M> {
+    if binary_message.len() < HEADER_FORMAT_LEN {
+        return Err(Error::RecvInvalidMsg(
+            crate::errors::IncomingMsgErr::MessageTooShort,
+        ));
+    }
+
+    // Read format
+    let message_format = MessageFormat::try_from(&binary_message[..HEADER_FORMAT_LEN])?;
+
+    deserialize_inner(&binary_message[HEADER_FORMAT_LEN..], message_format)
+        .map_err(Error::SerdeError)
+}
+
+pub fn deserialize_inner<M>(
+    binary_message: &[u8],
+    message_format: MessageFormat,
+) -> std::result::Result<M, SerdeError>
+where
+    M: Debug + DeserializeOwned,
+{
+    match message_format {
+        MessageFormat::RawBinary => Err(SerdeError::UseSuffixedBinFunctions),
+        #[cfg(feature = "bin")]
+        MessageFormat::Bincode => Ok(bincode::deserialize::<M>(binary_message)
+            .map_err(|e| SerdeError::BincodeError(format!("{}", e)))?),
+        #[cfg(feature = "cbor")]
+        MessageFormat::Cbor => {
+            Ok(serde_cbor::from_slice::<M>(binary_message).map_err(SerdeError::CborError)?)
+        }
+        #[cfg(feature = "json")]
+        MessageFormat::Utf8Json => {
+            Ok(serde_json::from_slice::<M>(binary_message).map_err(SerdeError::JsonError)?)
+        }
+        _ => unimplemented!(),
+    }
+}
diff --git a/lib/tools/pkstl/src/complete/serde/serializer.rs b/lib/tools/pkstl/src/complete/serde/serializer.rs
new file mode 100644
index 00000000..5a89e846
--- /dev/null
+++ b/lib/tools/pkstl/src/complete/serde/serializer.rs
@@ -0,0 +1,121 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Define PKSTL serializer.
+
+use super::SerdeError;
+use crate::format::MessageFormat;
+use crate::{Error, Result, SecureLayer};
+use serde::Serialize;
+use std::io::{BufWriter, Write};
+
+pub(crate) fn write_connect_msg<M, W>(
+    sl: &mut SecureLayer,
+    custom_datas: Option<&M>,
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    M: Serialize,
+    W: Write,
+{
+    // Serialize and compress custom datas
+    let custom_datas = if let Some(custom_datas) = custom_datas {
+        let bin_msg = serialize(custom_datas, sl.config.message_format)?;
+        Some(sl.compress(&bin_msg[..])?)
+    } else {
+        None
+    };
+
+    // Write binary message on a writer
+    crate::complete::writer::write_connect_msg::<W>(sl, custom_datas, writer)
+}
+
+pub(crate) fn write_ack_msg<M, W>(
+    sl: &mut SecureLayer,
+    custom_datas: Option<&M>,
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    M: Serialize,
+    W: Write,
+{
+    // Serialize and compress custom datas
+    let custom_datas = if let Some(custom_datas) = custom_datas {
+        let bin_msg = serialize(custom_datas, sl.config.message_format)?;
+        Some(sl.compress(&bin_msg[..])?)
+    } else {
+        None
+    };
+
+    // Write binary message on a writer
+    crate::complete::writer::write_ack_msg::<W>(sl, custom_datas, writer)
+}
+
+pub(crate) fn write_message<M, W>(
+    sl: &mut SecureLayer,
+    message: &M,
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    M: Serialize,
+    W: Write,
+{
+    // Serialize message
+    let bin_msg = serialize(message, sl.config.message_format)?;
+
+    // Compress message
+    let bin_zip_msg = sl.compress(&bin_msg[..])?;
+
+    // Write binary message on a writer
+    crate::complete::writer::write_bin_message::<W>(sl, &bin_zip_msg, writer)
+}
+
+pub fn serialize<M>(message: &M, message_format: MessageFormat) -> Result<Vec<u8>>
+where
+    M: Serialize,
+{
+    let mut writer = BufWriter::new(Vec::with_capacity(1_024));
+    writer
+        .write(message_format.as_ref())
+        .map_err(Error::WriteError)?;
+    serialize_inner(message, message_format, &mut writer).map_err(Error::SerdeError)?;
+    writer.into_inner().map_err(|_| Error::BufferFlushError)
+}
+
+pub fn serialize_inner<M, W>(
+    message: &M,
+    message_format: MessageFormat,
+    writer: &mut W,
+) -> std::result::Result<(), SerdeError>
+where
+    M: Serialize,
+    W: Write,
+{
+    match message_format {
+        MessageFormat::RawBinary => Err(SerdeError::UseSuffixedBinFunctions),
+        #[cfg(feature = "bin")]
+        MessageFormat::Bincode => Ok(bincode::serialize_into(writer, message)
+            .map_err(|e| SerdeError::BincodeError(format!("{}", e)))?),
+        #[cfg(feature = "cbor")]
+        MessageFormat::Cbor => {
+            Ok(serde_cbor::to_writer(writer, message).map_err(SerdeError::CborError)?)
+        }
+        #[cfg(feature = "json")]
+        MessageFormat::Utf8Json => {
+            Ok(serde_json::to_writer(writer, message).map_err(SerdeError::JsonError)?)
+        }
+        _ => unimplemented!(),
+    }
+}
diff --git a/lib/tools/pkstl/src/complete/writer.rs b/lib/tools/pkstl/src/complete/writer.rs
new file mode 100644
index 00000000..34d8a9b4
--- /dev/null
+++ b/lib/tools/pkstl/src/complete/writer.rs
@@ -0,0 +1,102 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage complete Public Key Secure Transport Layer.
+//! Sub-module define write operations.
+
+use super::SecureLayer;
+use crate::{Error, Result};
+use ring::signature::KeyPair;
+use std::io::{BufWriter, Write};
+
+#[inline]
+pub fn write_connect_msg<W>(
+    sl: &mut SecureLayer,
+    custom_datas: Option<Vec<u8>>,
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    W: Write,
+{
+    // Create connect message
+    let bin_connect_msg = sl.minimal_secure_layer.create_connect_message(
+        sl.sig_key_pair.public_key().as_ref(),
+        match custom_datas {
+            Some(ref d) => Some(&d[..]),
+            None => None,
+        },
+    )?;
+
+    // Write connect message
+    writer
+        .write(&bin_connect_msg)
+        .map_err(|_| Error::BufferFlushError)?;
+
+    // Sign message and write signature
+    sign_bin_msg_and_write_sig(sl, &bin_connect_msg, writer)
+}
+
+#[inline]
+pub fn write_ack_msg<W>(
+    sl: &mut SecureLayer,
+    custom_datas: Option<Vec<u8>>,
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    W: Write,
+{
+    // Create ack message
+    let bin_connect_msg = sl
+        .minimal_secure_layer
+        .create_ack_message(match custom_datas {
+            Some(ref d) => Some(&d[..]),
+            None => None,
+        })?;
+
+    // Write ack message
+    writer
+        .write(&bin_connect_msg)
+        .map_err(|_| Error::BufferFlushError)?;
+
+    // Sign message and write signature
+    sign_bin_msg_and_write_sig(sl, &bin_connect_msg, writer)
+}
+
+#[inline]
+fn sign_bin_msg_and_write_sig<W>(
+    sl: &mut SecureLayer,
+    bin_msg: &[u8],
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    W: Write,
+{
+    writer
+        .write(sl.sig_key_pair.sign(bin_msg).as_ref())
+        .map(|_| ())
+        .map_err(|_| Error::BufferFlushError)
+}
+
+#[inline]
+pub fn write_bin_message<W>(
+    sl: &mut SecureLayer,
+    message: &[u8],
+    writer: &mut BufWriter<W>,
+) -> Result<()>
+where
+    W: Write,
+{
+    sl.minimal_secure_layer.write_message(message, writer)
+}
diff --git a/lib/tools/pkstl/src/config.rs b/lib/tools/pkstl/src/config.rs
new file mode 100644
index 00000000..c80949b0
--- /dev/null
+++ b/lib/tools/pkstl/src/config.rs
@@ -0,0 +1,78 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage PKSTL configuration.
+
+use crate::encryption::EncryptAlgo;
+
+#[cfg(feature = "zip-sign")]
+const DEFAULT_COMPRESSION_MIN_SIZE: usize = 8_192;
+
+#[cfg(feature = "ser")]
+use crate::format::MessageFormat;
+
+#[cfg(feature = "zip-sign")]
+#[derive(Clone, Copy, Debug, PartialEq)]
+/// PKSTL Configuration
+pub struct SdtlConfig {
+    /// Compression level
+    pub compression: flate2::Compression,
+    /// Compression minimal size in bytes
+    pub compression_min_size: usize,
+    #[cfg(feature = "ser")]
+    /// Message format
+    pub message_format: MessageFormat,
+    /// PKSTL minimum Configuration
+    pub minimal: SdtlMinimalConfig,
+}
+
+impl Default for SdtlConfig {
+    fn default() -> Self {
+        SdtlConfig {
+            compression: flate2::Compression::fast(),
+            compression_min_size: DEFAULT_COMPRESSION_MIN_SIZE,
+            #[cfg(feature = "ser")]
+            message_format: MessageFormat::default(),
+            minimal: SdtlMinimalConfig::default(),
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, Default, PartialEq)]
+/// PKSTL minimum Configuration
+pub struct SdtlMinimalConfig {
+    /// Encryption algorithm
+    pub encrypt_algo: EncryptAlgo,
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    #[test]
+    fn test_default_config() {
+        assert_eq!(
+            SdtlConfig {
+                compression: flate2::Compression::fast(),
+                compression_min_size: DEFAULT_COMPRESSION_MIN_SIZE,
+                #[cfg(feature = "ser")]
+                message_format: MessageFormat::default(),
+                minimal: SdtlMinimalConfig::default(),
+            },
+            SdtlConfig::default()
+        )
+    }
+}
diff --git a/lib/tools/pkstl/src/constants.rs b/lib/tools/pkstl/src/constants.rs
new file mode 100644
index 00000000..be53fe19
--- /dev/null
+++ b/lib/tools/pkstl/src/constants.rs
@@ -0,0 +1,49 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Declare PKSTL constants.
+
+/// Sig algo length
+pub const SIG_ALGO_LEN: usize = 4;
+
+/// Current version
+pub(crate) const CURRENT_VERSION: [u8; 4] = [0, 0, 0, 1];
+
+/// Challenge size
+pub(crate) const CHALLENGE_SIZE: usize = 32;
+
+/// Hash size
+pub(crate) const HASH_SIZE: usize = 32;
+
+/// Ephemeral public key size
+pub(crate) const EPK_SIZE: usize = 32;
+
+/// Magic value (at the beginning of all messages)
+pub(crate) const MAGIC_VALUE: [u8; 4] = [0xE2, 0xC2, 0xE2, 0xD2];
+
+/// Message type length
+pub(crate) const MSG_TYPE_LEN: usize = 2;
+
+/// User message type
+pub(crate) const USER_MSG_TYPE: &[u8] = &[0, 0];
+
+/// Connect message type
+pub(crate) const CONNECT_MSG_TYPE: &[u8] = &[0, 1];
+
+/// Ack message type
+pub(crate) const ACK_MSG_TYPE: &[u8] = &[0, 2];
+
+/// Sig pubkey begin
+pub(crate) const SIG_PUBKEY_BEGIN: usize = MSG_TYPE_LEN + EPK_SIZE + SIG_ALGO_LEN;
diff --git a/lib/tools/pkstl/src/digest.rs b/lib/tools/pkstl/src/digest.rs
new file mode 100644
index 00000000..39b79550
--- /dev/null
+++ b/lib/tools/pkstl/src/digest.rs
@@ -0,0 +1,20 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage cryptographic digest operations.
+
+pub(crate) fn sha256(datas: &[u8]) -> impl AsRef<[u8]> {
+    ring::digest::digest(&ring::digest::SHA256, datas)
+}
diff --git a/lib/tools/pkstl/src/encryption.rs b/lib/tools/pkstl/src/encryption.rs
new file mode 100644
index 00000000..b5da36a7
--- /dev/null
+++ b/lib/tools/pkstl/src/encryption.rs
@@ -0,0 +1,158 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage cryptographic encryption operations.
+
+mod chacha20_poly1305_aead;
+
+use crate::agreement::{SharedSecret, SharedSecretLen};
+use crate::Result;
+use std::io::{BufWriter, Read, Write};
+
+/// Encryption algorithm
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum EncryptAlgo {
+    /// ChaCha20 stream cipher use the Poly1305 authenticator with Associated Data (AEAD) algorithm (see https://tools.ietf.org/html/rfc7539).
+    Chacha20Poly1305Aead,
+}
+
+impl Default for EncryptAlgo {
+    fn default() -> Self {
+        Self::Chacha20Poly1305Aead
+    }
+}
+
+impl EncryptAlgo {
+    pub(crate) fn shared_secret_len(self) -> SharedSecretLen {
+        match self {
+            Self::Chacha20Poly1305Aead => SharedSecretLen::B48,
+        }
+    }
+}
+
+pub enum EncryptAlgoWithSecretKey {
+    Chacha20Poly1305Aead(chacha20_poly1305_aead::SecretKey),
+}
+
+impl EncryptAlgoWithSecretKey {
+    pub fn build(encrypt_algo: EncryptAlgo, shared_secret: SharedSecret) -> Self {
+        match encrypt_algo {
+            EncryptAlgo::Chacha20Poly1305Aead => {
+                if let SharedSecret::B48(seed) = shared_secret {
+                    Self::Chacha20Poly1305Aead(chacha20_poly1305_aead::SecretKey::new(&seed))
+                } else {
+                    panic!("dev error: EncryptAlgo::Chacha20Poly1305Aead must request shared secret of 48 bytes !")
+                }
+            }
+        }
+    }
+}
+
+#[inline]
+pub(crate) fn decrypt<W: Write>(
+    encrypted_datas: &[u8],
+    algo_with_secret_key: &EncryptAlgoWithSecretKey,
+    writer: &mut BufWriter<W>,
+) -> Result<()> {
+    match algo_with_secret_key {
+        EncryptAlgoWithSecretKey::Chacha20Poly1305Aead(secret_key) => {
+            chacha20_poly1305_aead::decrypt(encrypted_datas, secret_key, writer)
+        }
+    }
+}
+
+/// Encrypt datas
+#[inline]
+pub(crate) fn encrypt<R: Read, W: Write>(
+    reader: &mut R,
+    algo_with_secret_key: &EncryptAlgoWithSecretKey,
+    writer: &mut BufWriter<W>,
+) -> Result<()> {
+    match algo_with_secret_key {
+        EncryptAlgoWithSecretKey::Chacha20Poly1305Aead(secret_key) => {
+            chacha20_poly1305_aead::encrypt(reader, secret_key, writer)
+        }
+    }
+}
+
+#[cfg(test)]
+pub mod tests {
+
+    use super::*;
+    use crate::seeds::{tests::random_seed_48, Seed32, Seed48};
+
+    pub fn gen_random_encrypt_algo_with_secret() -> EncryptAlgoWithSecretKey {
+        let random_shared_secret = SharedSecret::B48(random_seed_48());
+        EncryptAlgoWithSecretKey::build(EncryptAlgo::Chacha20Poly1305Aead, random_shared_secret)
+    }
+
+    #[test]
+    fn test_default() {
+        assert_eq!(EncryptAlgo::Chacha20Poly1305Aead, EncryptAlgo::default());
+    }
+
+    #[test]
+    #[should_panic(
+        expected = "dev error: EncryptAlgo::Chacha20Poly1305Aead must request shared secret of 48 bytes !"
+    )]
+    fn test_encryption_with_wrong_shared_secret_len() {
+        let shared_secret = SharedSecret::B32(Seed32::new([
+            0u8, 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,
+        ]));
+        EncryptAlgoWithSecretKey::build(EncryptAlgo::Chacha20Poly1305Aead, shared_secret);
+    }
+
+    #[test]
+    fn test_encryption_ok() -> Result<()> {
+        let datas = b"My secret datas".to_vec();
+
+        let shared_secret = SharedSecret::B48(Seed48::new([
+            0u8, 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,
+        ]));
+        let encrypt_algo_with_secret_key =
+            EncryptAlgoWithSecretKey::build(EncryptAlgo::Chacha20Poly1305Aead, shared_secret);
+
+        let mut encrypted_datas = BufWriter::new(Vec::with_capacity(datas.len()));
+
+        encrypt(
+            &mut &datas[..],
+            &encrypt_algo_with_secret_key,
+            &mut encrypted_datas,
+        )?;
+        let encrypted_datas = encrypted_datas
+            .into_inner()
+            .expect("fail to flush encrypt buffer");
+
+        let mut decrypted_datas = BufWriter::new(Vec::with_capacity(datas.len()));
+        decrypt(
+            &encrypted_datas,
+            &encrypt_algo_with_secret_key,
+            &mut decrypted_datas,
+        )?;
+        let decrypted_datas = decrypted_datas
+            .into_inner()
+            .expect("fail to flush decrypt buffer");
+
+        println!("encrypted_datas={:?}", encrypted_datas);
+        println!("decrypted_datas={:?}", decrypted_datas);
+
+        assert_eq!(datas, decrypted_datas);
+
+        Ok(())
+    }
+}
diff --git a/lib/crypto/src/encryption.rs b/lib/tools/pkstl/src/encryption/chacha20_poly1305_aead.rs
similarity index 53%
rename from lib/crypto/src/encryption.rs
rename to lib/tools/pkstl/src/encryption/chacha20_poly1305_aead.rs
index 33a0eb74..9dd33c0c 100644
--- a/lib/crypto/src/encryption.rs
+++ b/lib/tools/pkstl/src/encryption/chacha20_poly1305_aead.rs
@@ -1,4 +1,4 @@
-//  Copyright (C) 2017-2019  The AXIOM TEAM Association.
+//  Copyright (C) 2019  Eloïs SANCHEZ.
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Affero General Public License as
@@ -13,12 +13,12 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-//! Manage cryptographic encryption operations.
+//! Manage cryptographic encryption operations with Chacha20Poly1305Aead algorithm.
 
-use crate::errors::CryptoError;
 use crate::seeds::Seed48;
+use crate::{Error, Result};
 use clear_on_drop::clear::Clear;
-use std::io::Read;
+use std::io::{BufWriter, Read, Write};
 
 const CHACHA20_TAG_SIZE: usize = 16;
 
@@ -33,9 +33,9 @@ pub struct SecretKey {
 impl Drop for SecretKey {
     #[inline]
     fn drop(&mut self) {
-        self.key.clear();
-        self.nonce.clear();
-        self.aad.clear();
+        <[u8; 32] as Clear>::clear(&mut self.key);
+        <[u8; 12] as Clear>::clear(&mut self.nonce);
+        <[u8; 4] as Clear>::clear(&mut self.aad);
     }
 }
 
@@ -53,85 +53,76 @@ impl SecretKey {
 }
 
 /// Decrypt datas
-pub fn decrypt(encrypted_datas: &[u8], secret_key: &SecretKey) -> Result<Vec<u8>, CryptoError> {
+pub fn decrypt<W: Write>(
+    encrypted_datas: &[u8],
+    secret_key: &SecretKey,
+    writer: &mut BufWriter<W>,
+) -> Result<()> {
     let payload_len = encrypted_datas.len() - CHACHA20_TAG_SIZE;
 
-    let mut decrypted_datas = Vec::with_capacity(payload_len);
-
     chacha20_poly1305_aead::decrypt(
         &secret_key.key,
         &secret_key.nonce,
         &secret_key.aad,
         &encrypted_datas[0..payload_len],
         &encrypted_datas[payload_len..],
-        &mut decrypted_datas,
+        writer,
     )
-    .map_err(CryptoError::FailToDecryptDatas)?;
+    .map_err(Error::FailToDecryptDatas)?;
 
-    Ok(decrypted_datas)
+    Ok(())
 }
 
 /// Encrypt datas
-pub fn encrypt(datas: &[u8], secret_key: &SecretKey) -> Result<Vec<u8>, CryptoError> {
-    let mut encrypted_datas = Vec::with_capacity(datas.len() + CHACHA20_TAG_SIZE);
-
-    let tag = chacha20_poly1305_aead::encrypt(
-        &secret_key.key,
-        &secret_key.nonce,
-        &secret_key.aad,
-        datas,
-        &mut encrypted_datas,
-    )
-    .map_err(CryptoError::FailToEncryptDatas)?;
-
-    encrypted_datas.append(&mut tag.to_vec());
-
-    Ok(encrypted_datas)
-}
-
-/// Encrypt datas from reader
-pub fn encrypt_read<R: Read>(
-    datas_max_size: usize,
+pub fn encrypt<R: Read, W: Write>(
     reader: &mut R,
     secret_key: &SecretKey,
-) -> Result<Vec<u8>, CryptoError> {
-    let mut encrypted_datas = Vec::with_capacity(datas_max_size + CHACHA20_TAG_SIZE);
-
+    writer: &mut BufWriter<W>,
+) -> Result<()> {
     let tag = chacha20_poly1305_aead::encrypt_read(
         &secret_key.key,
         &secret_key.nonce,
         &secret_key.aad,
         reader,
-        &mut encrypted_datas,
+        writer,
     )
-    .map_err(CryptoError::FailToEncryptDatas)?;
+    .map_err(Error::FailToEncryptDatas)?;
 
-    encrypted_datas.append(&mut tag.to_vec());
+    writer
+        .write(&tag.to_vec())
+        .map_err(Error::FailToEncryptDatas)?;
 
-    Ok(encrypted_datas)
+    Ok(())
 }
 
 #[cfg(test)]
 mod tests {
 
     use super::*;
+    use crate::seeds::Seed48;
 
     #[test]
-    fn test_encryption() -> Result<(), CryptoError> {
+    fn test_encryption() -> Result<()> {
         let datas = b"My secret datas".to_vec();
 
-        let secret_key = SecretKey::new(&Seed48::random());
+        let secret_key = SecretKey::new(&Seed48::new([
+            0u8, 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,
+        ]));
 
-        let encrypted_datas = encrypt(&datas, &secret_key)?;
-        let decrypted_datas = decrypt(&encrypted_datas, &secret_key)?;
+        let mut encrypted_datas = BufWriter::new(Vec::with_capacity(datas.len()));
 
-        println!("encrypted_datas={:?}", encrypted_datas);
-        println!("decrypted_datas={:?}", decrypted_datas);
-
-        assert_eq!(datas, decrypted_datas);
+        encrypt(&mut &datas[..], &secret_key, &mut encrypted_datas)?;
+        let encrypted_datas = encrypted_datas
+            .into_inner()
+            .expect("fail to flush encrypt buffer");
 
-        let encrypted_datas = encrypt_read(datas.len(), &mut &datas[..], &secret_key)?;
-        let decrypted_datas = decrypt(&encrypted_datas, &secret_key)?;
+        let mut decrypted_datas = BufWriter::new(Vec::with_capacity(datas.len()));
+        decrypt(&encrypted_datas, &secret_key, &mut decrypted_datas)?;
+        let decrypted_datas = decrypted_datas
+            .into_inner()
+            .expect("fail to flush decrypt buffer");
 
         println!("encrypted_datas={:?}", encrypted_datas);
         println!("decrypted_datas={:?}", decrypted_datas);
diff --git a/lib/tools/pkstl/src/errors.rs b/lib/tools/pkstl/src/errors.rs
new file mode 100644
index 00000000..d7069ed0
--- /dev/null
+++ b/lib/tools/pkstl/src/errors.rs
@@ -0,0 +1,106 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage Secure and decentralized transport layer errors.
+
+/// PKSTL Error
+#[derive(Debug)]
+pub enum Error {
+    /// Error when flush writer buffer
+    BufferFlushError,
+    /// The connection had already failed earlier
+    ConnectionHadFail,
+    /// Connect msg already written
+    ConnectMsgAlreadyWritten,
+    /// Fail to compute agreement
+    FailToComputeAgreement,
+    /// Fail to decrypt datas
+    FailToDecryptDatas(chacha20_poly1305_aead::DecryptError),
+    /// Fail to encrypt datas
+    FailToEncryptDatas(std::io::Error),
+    /// Fail to generate ephemeral key pair
+    FailToGenEphemerKeyPair,
+    /// Fail to generate ephemeral public key
+    FailToGenEphemerPubKey,
+    /// Fail to generate signature key pair
+    FailtoGenSigKeyPair,
+    /// Forbidden to write the ACK message now
+    ForbidWriteAckMsgNow,
+    /// Message must be signed
+    MessageMustBeSigned,
+    /// The negotiation must have been successful
+    NegoMustHaveBeenSuccessful,
+    #[cfg(feature = "ser")]
+    /// Error in serialization/deserialization
+    SerdeError(crate::complete::serde::SerdeError),
+    /// Serialization error
+    SerializationError(std::io::Error),
+    /// Tru to generate connect message too late
+    TryToGenConnectMsgTooLate,
+    /// Trying to write a message when the negotiation is not successful
+    TryToWriteMsgWhenNegoNotSuccessful,
+    /// Receive invalid message
+    RecvInvalidMsg(IncomingMsgErr),
+    /// Unexpected remote signature public key
+    UnexpectedRemoteSigPubKey,
+    /// Error on writer
+    WriteError(std::io::Error),
+    /// Written length error
+    WrittenLenError {
+        /// Expected
+        expected: usize,
+        /// Found
+        found: usize,
+    },
+    #[cfg(feature = "zip-sign")]
+    /// Compression or decompression error
+    ZipError(std::io::Error),
+}
+
+impl From<IncomingMsgErr> for Error {
+    fn from(e: IncomingMsgErr) -> Self {
+        Self::RecvInvalidMsg(e)
+    }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// Incoming message error
+pub enum IncomingMsgErr {
+    /// Invalid challenge
+    InvalidChallenge,
+    /// Invalid hash or signature
+    InvalidHashOrSig,
+    /// Invalid magic value
+    InvalidMagicValue,
+    /// Message too short
+    MessageTooShort,
+    /// Unexpected a cck message
+    UnexpectedAckMsg,
+    /// Unexpected connect message
+    UnexpectedConnectMsg,
+    /// Unexpected user message
+    UnexpectedMessage,
+    /// Unexpected encryption state
+    /// It may be that the message is in clear when we expect it to be encrypted.
+    UnexpectedEncryptionState,
+    /// Unknown message format
+    UnknownMessageFormat,
+    /// Unknown message type
+    UnknownMessageType,
+    /// Unsupported signature algorithm
+    UnsupportedSigAlgo,
+    /// Unsupported version
+    UnsupportedVersion,
+}
diff --git a/lib/tools/pkstl/src/format.rs b/lib/tools/pkstl/src/format.rs
new file mode 100644
index 00000000..cb85a459
--- /dev/null
+++ b/lib/tools/pkstl/src/format.rs
@@ -0,0 +1,156 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage PKSTL messages format.
+
+use crate::errors::IncomingMsgErr;
+use std::convert::TryFrom;
+
+const RAW_BINARY: &[u8] = &[0, 0, 0, 0];
+const UTF8_PLAIN_TEXT: &[u8] = &[0, 0, 0, 1];
+
+#[cfg(feature = "bin")]
+const BINCODE: &[u8] = &[0, 0, 0, 4];
+
+#[cfg(feature = "cbor")]
+const CBOR: &[u8] = &[0, 0, 0, 3];
+
+#[cfg(feature = "json")]
+const UTF8_JSON: &[u8] = &[0, 0, 0, 2];
+
+/// Message format
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum MessageFormat {
+    /// raw binary
+    RawBinary,
+    /// UTF-8 plain text
+    Utf8PlainText,
+    #[cfg(feature = "json")]
+    /// UTF-8 JSON
+    Utf8Json,
+    #[cfg(feature = "cbor")]
+    /// CBOR (Binary JSON)
+    Cbor,
+    #[cfg(feature = "bin")]
+    /// Bincode
+    Bincode,
+}
+
+impl Default for MessageFormat {
+    fn default() -> Self {
+        Self::RawBinary
+    }
+}
+
+impl TryFrom<&[u8]> for MessageFormat {
+    type Error = IncomingMsgErr;
+
+    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
+        match bytes {
+            #[cfg(feature = "bin")]
+            BINCODE => Ok(Self::Bincode),
+            #[cfg(feature = "cbor")]
+            CBOR => Ok(Self::Cbor),
+            RAW_BINARY => Ok(Self::RawBinary),
+            #[cfg(feature = "json")]
+            UTF8_JSON => Ok(Self::Utf8Json),
+            UTF8_PLAIN_TEXT => Ok(Self::Utf8PlainText),
+            _ => Err(IncomingMsgErr::UnknownMessageFormat),
+        }
+    }
+}
+
+impl AsRef<[u8]> for MessageFormat {
+    fn as_ref(&self) -> &[u8] {
+        match self {
+            #[cfg(feature = "bin")]
+            Self::Bincode => BINCODE,
+            #[cfg(feature = "cbor")]
+            Self::Cbor => CBOR,
+            Self::RawBinary => RAW_BINARY,
+            #[cfg(feature = "json")]
+            Self::Utf8Json => UTF8_JSON,
+            Self::Utf8PlainText => UTF8_PLAIN_TEXT,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    #[test]
+    fn test_default() {
+        assert_eq!(MessageFormat::RawBinary, MessageFormat::default());
+    }
+
+    #[test]
+    fn test_messafe_format_as_ref() {
+        // RawBinary
+        assert_eq!(RAW_BINARY, MessageFormat::RawBinary.as_ref());
+
+        // Utf8PlainText
+        assert_eq!(UTF8_PLAIN_TEXT, MessageFormat::Utf8PlainText.as_ref());
+
+        // Utf8Json
+        #[cfg(feature = "json")]
+        assert_eq!(UTF8_JSON, MessageFormat::Utf8Json.as_ref());
+
+        // Cbor
+        #[cfg(feature = "cbor")]
+        assert_eq!(CBOR, MessageFormat::Cbor.as_ref());
+
+        // Bincode
+        #[cfg(feature = "bin")]
+        assert_eq!(BINCODE, MessageFormat::Bincode.as_ref());
+    }
+
+    #[test]
+    fn test_message_format_try_from() -> Result<(), IncomingMsgErr> {
+        // RawBinary
+        assert_eq!(
+            MessageFormat::RawBinary,
+            MessageFormat::try_from(RAW_BINARY)?
+        );
+
+        // Utf8PlainText
+        assert_eq!(
+            MessageFormat::Utf8PlainText,
+            MessageFormat::try_from(UTF8_PLAIN_TEXT)?
+        );
+
+        // Utf8Json
+        #[cfg(feature = "json")]
+        assert_eq!(MessageFormat::Utf8Json, MessageFormat::try_from(UTF8_JSON)?);
+
+        // Cbor
+        #[cfg(feature = "cbor")]
+        assert_eq!(MessageFormat::Cbor, MessageFormat::try_from(CBOR)?);
+
+        // Bincode
+        #[cfg(feature = "bin")]
+        assert_eq!(MessageFormat::Bincode, MessageFormat::try_from(BINCODE)?);
+
+        // UnknownMessageFormat
+        let bytes = vec![0, 0, 0, 5];
+        assert_eq!(
+            Err(IncomingMsgErr::UnknownMessageFormat),
+            MessageFormat::try_from(&bytes[..]),
+        );
+
+        Ok(())
+    }
+}
diff --git a/lib/tools/pkstl/src/lib.rs b/lib/tools/pkstl/src/lib.rs
new file mode 100644
index 00000000..09e8bc5b
--- /dev/null
+++ b/lib/tools/pkstl/src/lib.rs
@@ -0,0 +1,100 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Public Key Secure Transport Layer.
+
+#![deny(
+    clippy::option_unwrap_used,
+    clippy::result_unwrap_used,
+    missing_docs,
+    missing_copy_implementations,
+    trivial_casts,
+    trivial_numeric_casts,
+    unsafe_code,
+    unstable_features,
+    unused_import_braces,
+    unused_qualifications
+)]
+
+mod agreement;
+#[cfg(feature = "zip-sign")]
+mod complete;
+mod config;
+mod constants;
+mod digest;
+mod encryption;
+mod errors;
+#[cfg(feature = "ser")]
+mod format;
+mod message;
+mod minimal;
+mod reader;
+mod seeds;
+mod signature;
+mod status;
+
+pub use agreement::EphemeralPublicKey;
+pub use config::{SdtlConfig, SdtlMinimalConfig};
+pub use encryption::EncryptAlgo;
+pub use errors::Error;
+pub use message::{EncapsuledMessage, Message};
+pub use minimal::MinimalSecureLayer;
+pub use seeds::Seed32;
+pub use signature::{SIG_ALGO_ED25519, SIG_ALGO_ED25519_ARRAY};
+
+#[cfg(feature = "ser")]
+pub use complete::IncomingMessage;
+#[cfg(feature = "ser")]
+pub use format::MessageFormat;
+
+#[cfg(feature = "zip-sign")]
+pub use complete::message::IncomingBinaryMessage;
+#[cfg(feature = "zip-sign")]
+pub use complete::SecureLayer;
+
+/// PKSTL Result
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub(crate) enum LocalNegoThread {
+    Created,
+    ConnectMsgSent,
+    ValidAckMsgReceived,
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub(crate) enum RemoteNegoThread {
+    WaitConnectMsg,
+    ValidConnectMsgReceived,
+    AckMsgSent,
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum MsgType {
+    Connect,
+    Ack,
+    UserMsg,
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum Action {
+    Create(MsgType),
+    Receive(MsgType),
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum ActionSideEffects {
+    PushUserMsgIntoTmpStack,
+}
diff --git a/lib/tools/pkstl/src/message.rs b/lib/tools/pkstl/src/message.rs
new file mode 100644
index 00000000..0ecf9f4d
--- /dev/null
+++ b/lib/tools/pkstl/src/message.rs
@@ -0,0 +1,494 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage PKSTL messages.
+
+use crate::constants::*;
+use crate::digest::sha256;
+use crate::{Error, Result};
+use std::io::{BufWriter, Write};
+
+const CONNECT_MSG_TYPE_HEADERS_SIZE: usize = 70;
+const ACK_MSG_TYPE_HEADERS_SIZE: usize = 34;
+
+const HEADERS_AND_FOOTERS_MAX_SIZE: usize = 136;
+
+/// Message
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Message {
+    /// Connect message
+    Connect {
+        /// Signature algorithm
+        sig_algo: [u8; SIG_ALGO_LEN],
+        /// Signature public key
+        sig_pubkey: Vec<u8>,
+        /// Custom datas
+        custom_datas: Option<Vec<u8>>,
+    },
+    /// Ack Message
+    Ack {
+        /// Custom datas
+        custom_datas: Option<Vec<u8>>,
+    },
+    /// Message
+    Message {
+        /// Custom datas
+        custom_datas: Option<Vec<u8>>,
+    },
+}
+
+/// Message that referencing datas
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum MessageRef<'a> {
+    /// Connect message
+    Connect {
+        /// Signature algorithm
+        sig_algo: [u8; SIG_ALGO_LEN],
+        /// Signature public key
+        sig_pubkey: Vec<u8>,
+        /// Custom datas
+        custom_datas: Option<&'a [u8]>,
+    },
+    /// Ack Message
+    Ack {
+        /// Custom datas
+        custom_datas: Option<&'a [u8]>,
+    },
+    /// Message
+    Message {
+        /// Custom datas
+        custom_datas: Option<&'a [u8]>,
+    },
+}
+
+/// Encapsuled message
+#[derive(Debug, PartialEq)]
+pub struct EncapsuledMessage {
+    pub(crate) datas: Vec<u8>,
+}
+
+impl AsRef<[u8]> for EncapsuledMessage {
+    fn as_ref(&self) -> &[u8] {
+        &self.datas[..]
+    }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub(crate) enum MsgTypeHeaders {
+    Connect {
+        peer_ephemeral_pk: [u8; EPK_SIZE],
+        sig_algo: [u8; SIG_ALGO_LEN],
+        sig_pubkey: Vec<u8>,
+    },
+    Ack {
+        challenge: [u8; CHALLENGE_SIZE],
+    },
+    UserMsg,
+}
+
+impl MsgTypeHeaders {
+    pub(crate) fn must_be_encrypted(&self) -> bool {
+        if let MsgTypeHeaders::UserMsg = self {
+            true
+        } else {
+            false
+        }
+    }
+}
+
+struct InnerPreparedMsg<'a> {
+    bin_user_msg: Option<&'a [u8]>,
+    type_msg_headers: Vec<u8>,
+}
+
+impl Message {
+    pub(crate) fn from_bytes(msg_bytes: Vec<u8>, msg_type_headers: MsgTypeHeaders) -> Result<Self> {
+        // Read custom datas
+        let custom_datas = if !msg_bytes.is_empty() {
+            Some(msg_bytes)
+        } else {
+            None
+        };
+
+        // Build message according to it's type
+        match msg_type_headers {
+            MsgTypeHeaders::UserMsg => Ok(Message::Message { custom_datas }),
+            MsgTypeHeaders::Connect {
+                sig_algo,
+                sig_pubkey,
+                ..
+            } => Ok(Message::Connect {
+                sig_algo,
+                sig_pubkey,
+                custom_datas,
+            }),
+            MsgTypeHeaders::Ack { .. } => Ok(Message::Ack { custom_datas }),
+        }
+    }
+}
+
+impl<'a> MessageRef<'a> {
+    #[inline]
+    fn prepare_message(
+        &self,
+        self_epk: &[u8],
+        peer_epk: Option<&Vec<u8>>,
+    ) -> Result<InnerPreparedMsg<'a>> {
+        match self {
+            Self::Connect {
+                sig_algo,
+                sig_pubkey,
+                custom_datas,
+            } => {
+                // type message headers
+                let mut type_msg_headers = Vec::with_capacity(CONNECT_MSG_TYPE_HEADERS_SIZE);
+                type_msg_headers
+                    .write(CONNECT_MSG_TYPE)
+                    .map_err(Error::WriteError)?;
+                type_msg_headers
+                    .write(self_epk)
+                    .map_err(Error::WriteError)?;
+                type_msg_headers
+                    .write(&sig_algo[..])
+                    .map_err(Error::WriteError)?;
+                type_msg_headers
+                    .write(sig_pubkey)
+                    .map_err(Error::WriteError)?;
+
+                Ok(InnerPreparedMsg {
+                    bin_user_msg: *custom_datas,
+                    type_msg_headers,
+                })
+            }
+            Self::Ack { custom_datas } => {
+                // type message headers
+                let mut type_msg_headers = Vec::with_capacity(ACK_MSG_TYPE_HEADERS_SIZE);
+                type_msg_headers
+                    .write(ACK_MSG_TYPE)
+                    .map_err(Error::WriteError)?;
+                // write challenge
+                if let Some(peer_epk) = peer_epk {
+                    Self::write_challenge(peer_epk, &mut type_msg_headers)?;
+                } else {
+                    panic!("Dev error: try to write ack message before known peer epk.");
+                }
+
+                Ok(InnerPreparedMsg {
+                    bin_user_msg: *custom_datas,
+                    type_msg_headers,
+                })
+            }
+            Self::Message { custom_datas } => Ok(InnerPreparedMsg {
+                bin_user_msg: *custom_datas,
+                type_msg_headers: USER_MSG_TYPE.to_vec(),
+            }),
+        }
+    }
+    /// Convert message to bytes
+    pub(crate) fn to_bytes(
+        &self,
+        self_epk: &[u8],
+        peer_epk: Option<&Vec<u8>>,
+    ) -> Result<EncapsuledMessage> {
+        let InnerPreparedMsg {
+            bin_user_msg,
+            type_msg_headers,
+        } = self.prepare_message(self_epk, peer_epk)?;
+
+        let bin_user_msg_len = bin_user_msg.unwrap_or(&[]).len();
+
+        // Create temporary write buffer for datas that will then be signed or hashed
+        let mut bytes_will_signed_or_hashed = BufWriter::new(Vec::with_capacity(
+            bin_user_msg_len + HEADERS_AND_FOOTERS_MAX_SIZE,
+        ));
+
+        // Write MAGIC_VALUE
+        bytes_will_signed_or_hashed
+            .write(&MAGIC_VALUE)
+            .map_err(Error::WriteError)?;
+
+        // Write VERSION
+        bytes_will_signed_or_hashed
+            .write(&CURRENT_VERSION)
+            .map_err(Error::WriteError)?;
+
+        // Write ENCAPSULED_MSG_SIZE
+        let encapsuled_msg_size = type_msg_headers.len() + bin_user_msg.unwrap_or(&[]).len();
+        bytes_will_signed_or_hashed
+            .write(&(encapsuled_msg_size as u64).to_be_bytes())
+            .map_err(Error::WriteError)?;
+
+        // Write type message headers
+        bytes_will_signed_or_hashed
+            .write(&type_msg_headers)
+            .map_err(Error::WriteError)?;
+
+        // Write user message
+        if let Some(bin_user_msg) = bin_user_msg {
+            bytes_will_signed_or_hashed
+                .write(bin_user_msg)
+                .map_err(Error::WriteError)?;
+        }
+
+        // Flush bytes_will_signed buffer
+        let bytes_will_signed_or_hashed = bytes_will_signed_or_hashed
+            .into_inner()
+            .map_err(|_| Error::BufferFlushError)?;
+
+        // Return the bytes will signed
+        Ok(EncapsuledMessage {
+            datas: bytes_will_signed_or_hashed,
+        })
+    }
+    #[inline]
+    fn write_challenge<W: Write>(ephem_pk: &[u8], writer: &mut W) -> Result<()> {
+        writer
+            .write(sha256(ephem_pk).as_ref())
+            .map_err(Error::WriteError)?;
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn test_encapsuled_message() {
+        let encapsuled_msg = EncapsuledMessage {
+            datas: vec![1, 2, 3],
+        };
+
+        assert_eq!(&[1, 2, 3], encapsuled_msg.as_ref());
+    }
+
+    #[test]
+    fn test_connect_message_to_bytes() -> Result<()> {
+        let fake_epk = &[0u8; 32];
+
+        // Test connect message with custom datas
+        let message = MessageRef::Connect {
+            custom_datas: Some(&[5, 4, 4, 5]),
+            sig_algo: [0u8; SIG_ALGO_LEN],
+            sig_pubkey: vec![
+                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+                10, 11, 12, 13, 14, 15,
+            ],
+        };
+        assert_eq!(
+            EncapsuledMessage {
+                datas: vec![
+                    226, 194, 226, 210, // MAGIC_VALUE
+                    0, 0, 0, 1, // VERSION
+                    0, 0, 0, 0, 0, 0, 0, 74, // ENCAPSULED_MSG_LEN
+                    0, 1, // CONNECT_MSG_TYPE
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, // fake EPK (32 bytes)
+                    0, 0, 0, 0, // SIG_ALGO
+                    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7,
+                    8, 9, 10, 11, 12, 13, 14, 15, // fake SIG_PK (32 bytes)
+                    5, 4, 4, 5 // custom datas
+                ],
+            },
+            message.to_bytes(fake_epk, None)?
+        );
+
+        // Test connect message without custom datas
+        let message = MessageRef::Connect {
+            custom_datas: None,
+            sig_algo: [0u8; SIG_ALGO_LEN],
+            sig_pubkey: vec![
+                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+                10, 11, 12, 13, 14, 15,
+            ],
+        };
+
+        assert_eq!(
+            EncapsuledMessage {
+                datas: vec![
+                    226, 194, 226, 210, // MAGIC_VALUE
+                    0, 0, 0, 1, // VERSION
+                    0, 0, 0, 0, 0, 0, 0, 70, // ENCAPSULED_MSG_LEN
+                    0, 1, // CONNECT_MSG_TYPE
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, // fake EPK (32 bytes)
+                    0, 0, 0, 0, // SIG_ALGO
+                    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7,
+                    8, 9, 10, 11, 12, 13, 14, 15, // fake SIG_PK (32 bytes)
+                ],
+            },
+            message.to_bytes(fake_epk, None,)?
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ack_message_to_bytes() -> Result<()> {
+        let fake_epk = &[0u8; 32];
+
+        // Test ack message with custom datas
+        let message = MessageRef::Ack {
+            custom_datas: Some(&[5, 4, 4, 5]),
+        };
+        assert_eq!(
+            EncapsuledMessage {
+                datas: vec![
+                    226, 194, 226, 210, // MAGIC_VALUE
+                    0, 0, 0, 1, // VERSION
+                    0, 0, 0, 0, 0, 0, 0, 38, // ENCAPSULED_MSG_LEN
+                    0, 2, // ACK_MSG_TYPE
+                    102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32,
+                    8, 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41,
+                    37, // CHALLENGE (hash sha256)
+                    5, 4, 4, 5 // custom datas
+                ],
+            },
+            message.to_bytes(fake_epk, Some(&fake_epk.to_vec()),)?
+        );
+
+        // Test ack message without custom datas
+        let message = MessageRef::Ack { custom_datas: None };
+        assert_eq!(
+            EncapsuledMessage {
+                datas: vec![
+                    226, 194, 226, 210, // MAGIC_VALUE
+                    0, 0, 0, 1, // VERSION
+                    0, 0, 0, 0, 0, 0, 0, 34, // ENCAPSULED_MSG_LEN
+                    0, 2, // ACK_MSG_TYPE
+                    102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32,
+                    8, 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41,
+                    37, // CHALLENGE (hash sha256)
+                ],
+            },
+            message.to_bytes(fake_epk, Some(&fake_epk.to_vec()),)?
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    #[should_panic(expected = "Dev error: try to write ack message before known peer epk.")]
+    fn test_ack_message_to_bytes_before_recv_connect_msg() {
+        let fake_epk = &[0u8; 32];
+
+        // Test ack message without custom datas
+        let message = MessageRef::Ack { custom_datas: None };
+        let _ = message.to_bytes(fake_epk, None);
+    }
+
+    #[test]
+    fn test_user_message_to_bytes() -> Result<()> {
+        let fake_epk = &[0u8; 32];
+
+        // Test user message
+        let empty_user_message = MessageRef::Message {
+            custom_datas: Some(&[5, 4, 4, 5]),
+        };
+        assert_eq!(
+            EncapsuledMessage {
+                datas: vec![
+                    226, 194, 226, 210, // MAGIC_VALUE
+                    0, 0, 0, 1, // VERSION
+                    0, 0, 0, 0, 0, 0, 0, 6, // ENCAPSULED_MSG_LEN
+                    0, 0, // USER_MSG_TYPE
+                    5, 4, 4, 5 // custom datas
+                ],
+            },
+            empty_user_message.to_bytes(fake_epk, None)?
+        );
+
+        // Test empty user message
+        let empty_user_message = MessageRef::Message { custom_datas: None };
+        assert_eq!(
+            EncapsuledMessage {
+                datas: vec![
+                    226, 194, 226, 210, // MAGIC_VALUE
+                    0, 0, 0, 1, // VERSION
+                    0, 0, 0, 0, 0, 0, 0, 2, // ENCAPSULED_MSG_LEN
+                    0, 0, // USER_MSG_TYPE
+                ],
+            },
+            empty_user_message.to_bytes(fake_epk, None)?
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_message_from_bytes() -> Result<()> {
+        // Define message
+        let mut msg_bytes = vec![
+            3, 3, 3, 3, // user message
+        ];
+
+        // User message
+        assert_eq!(
+            Message::Message {
+                custom_datas: Some(vec![3, 3, 3, 3]),
+            },
+            Message::from_bytes(msg_bytes.clone(), MsgTypeHeaders::UserMsg)?
+        );
+
+        // Ack message
+        assert_eq!(
+            Message::Ack {
+                custom_datas: Some(vec![3, 3, 3, 3]),
+            },
+            Message::from_bytes(
+                msg_bytes.clone(),
+                MsgTypeHeaders::Ack {
+                    challenge: [0u8; CHALLENGE_SIZE]
+                }
+            )?
+        );
+
+        // Connect message
+        assert_eq!(
+            Message::Connect {
+                sig_pubkey: (0..31).collect(),
+                sig_algo: [0u8; SIG_ALGO_LEN],
+                custom_datas: Some(vec![3, 3, 3, 3]),
+            },
+            Message::from_bytes(
+                msg_bytes.clone(),
+                MsgTypeHeaders::Connect {
+                    peer_ephemeral_pk: [0u8; EPK_SIZE],
+                    sig_algo: [0u8; SIG_ALGO_LEN],
+                    sig_pubkey: (0..31).collect(),
+                }
+            )?
+        );
+
+        // Truncate user message content, msut be not panic
+        assert_eq!(
+            Message::Message {
+                custom_datas: Some(vec![3, 3])
+            },
+            Message::from_bytes(msg_bytes.drain(..2).collect(), MsgTypeHeaders::UserMsg)?,
+        );
+
+        // Empty message
+        let empty_msg_bytes = vec![];
+        assert_eq!(
+            Message::Message { custom_datas: None },
+            Message::from_bytes(empty_msg_bytes, MsgTypeHeaders::UserMsg)?
+        );
+
+        Ok(())
+    }
+}
diff --git a/lib/tools/pkstl/src/minimal.rs b/lib/tools/pkstl/src/minimal.rs
new file mode 100644
index 00000000..9ad02233
--- /dev/null
+++ b/lib/tools/pkstl/src/minimal.rs
@@ -0,0 +1,604 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Manage minimal secure and decentralized transport layer.
+
+use crate::agreement::{EphemeralKeyPair, EphemeralPublicKey};
+use crate::config::SdtlMinimalConfig;
+use crate::constants::*;
+use crate::digest::sha256;
+use crate::encryption::{encrypt, EncryptAlgoWithSecretKey};
+use crate::errors::IncomingMsgErr;
+use crate::message::{EncapsuledMessage, Message, MessageRef, MsgTypeHeaders};
+use crate::reader::{self, DecryptedIncomingDatas};
+use crate::signature::{self, SIG_ALGO_ED25519_ARRAY};
+use crate::status::SecureLayerStatus;
+use crate::{Action, ActionSideEffects, Error, MsgType, Result};
+use std::io::{BufReader, BufWriter, Write};
+
+/// Minimal secure layer
+pub struct MinimalSecureLayer {
+    ack_msg_recv_too_early: Option<Vec<u8>>,
+    config: SdtlMinimalConfig,
+    pub(crate) encrypt_algo_with_secret: Option<EncryptAlgoWithSecretKey>,
+    ephemeral_kp: Option<EphemeralKeyPair>,
+    pub(crate) ephemeral_pubkey: EphemeralPublicKey,
+    peer_epk: Option<Vec<u8>>,
+    peer_sig_pubkey: Option<Vec<u8>>,
+    pub(crate) status: SecureLayerStatus,
+    tmp_stack_user_msgs: Vec<Vec<u8>>,
+}
+
+impl MinimalSecureLayer {
+    /// Change configuration
+    pub fn change_config(&mut self, new_config: SdtlMinimalConfig) {
+        self.config = new_config;
+    }
+    /// Create minimal secure layer
+    pub fn create(
+        config: SdtlMinimalConfig,
+        expected_remote_sig_public_key: Option<Vec<u8>>,
+    ) -> Result<Self> {
+        let ephemeral_kp = EphemeralKeyPair::generate()?;
+        let ephemeral_pubkey = ephemeral_kp.public_key().clone();
+
+        let secure_layer = MinimalSecureLayer {
+            ack_msg_recv_too_early: None,
+            config,
+            encrypt_algo_with_secret: None,
+            ephemeral_pubkey,
+            ephemeral_kp: Some(ephemeral_kp),
+            peer_epk: None,
+            peer_sig_pubkey: expected_remote_sig_public_key,
+            status: SecureLayerStatus::init(),
+            tmp_stack_user_msgs: Vec::new(),
+        };
+
+        Ok(secure_layer)
+    }
+    pub(crate) fn compute_shared_secret(&mut self, peer_ephemeral_public_key: &[u8]) -> Result<()> {
+        let encrypt_algo = self.config.encrypt_algo;
+        let ephemeral_kp = self.ephemeral_kp.take();
+        if let Some(ephemeral_kp) = ephemeral_kp {
+            let shared_secret = ephemeral_kp.compute_shared_secret(
+                peer_ephemeral_public_key,
+                encrypt_algo.shared_secret_len(),
+            )?;
+
+            self.encrypt_algo_with_secret =
+                Some(EncryptAlgoWithSecretKey::build(encrypt_algo, shared_secret));
+
+            Ok(())
+        } else if self.encrypt_algo_with_secret.is_some() {
+            // Shared secret already computed, do nothing
+            Ok(())
+        } else {
+            unreachable!("dev error: fisrt call of compute_shared_secret() without ephemeral_kp !")
+        }
+    }
+    /// Drain temporary stack of remote messages
+    pub fn drain_tmp_stack_user_msgs(&mut self) -> Result<Vec<Message>> {
+        let bin_msgs: Vec<Vec<u8>> = self.tmp_stack_user_msgs.drain(..).collect();
+        let mut msgs = Vec::with_capacity(bin_msgs.len());
+        for bin_msg in bin_msgs {
+            if let Some(msg) = self.read_inner(&bin_msg, false)? {
+                msgs.push(msg);
+            }
+        }
+        Ok(msgs)
+    }
+    #[inline]
+    /// Encapsulate message
+    fn encapsulate_message(&mut self, message: &MessageRef) -> Result<EncapsuledMessage> {
+        message.to_bytes(&self.ephemeral_pubkey.as_ref(), self.peer_epk.as_ref())
+    }
+    /// Take ACK message received too early
+    #[inline]
+    pub fn take_ack_msg_recv_too_early(&mut self) -> Result<Option<Message>> {
+        if let Some(bin_ack_msg) = self.ack_msg_recv_too_early.take() {
+            self.read(&bin_ack_msg)
+        } else {
+            Ok(None)
+        }
+    }
+    #[inline]
+    /// Read incoming datas
+    pub fn read(&mut self, incoming_datas: &[u8]) -> Result<Option<Message>> {
+        self.read_inner(incoming_datas, true)
+    }
+    fn read_inner(
+        &mut self,
+        incoming_datas: &[u8],
+        check_encrypt_state: bool,
+    ) -> Result<Option<Message>> {
+        // Decrypt incoming messsage and parse headers
+        let DecryptedIncomingDatas {
+            mut datas,
+            user_msg_begin,
+            user_msg_end,
+            msg_type_headers,
+        } = match reader::read(
+            self.encrypt_algo_with_secret.as_ref(),
+            incoming_datas,
+            check_encrypt_state,
+        ) {
+            Ok(decrypted_incoming_datas) => decrypted_incoming_datas,
+            Err(e) => {
+                self.status = SecureLayerStatus::Fail;
+                return Err(e);
+            }
+        };
+
+        //println!("DEBUG TMP: msg_type_headers={:#?}", msg_type_headers);
+        match msg_type_headers {
+            MsgTypeHeaders::Connect {
+                peer_ephemeral_pk,
+                ref sig_pubkey,
+                ..
+            } => {
+                // Verify (or get) peer sig pubkey
+                if let Some(ref peer_sig_pubkey) = self.peer_sig_pubkey {
+                    if sig_pubkey != peer_sig_pubkey {
+                        return Err(Error::UnexpectedRemoteSigPubKey);
+                    }
+                } else {
+                    self.peer_sig_pubkey = Some(sig_pubkey.to_vec());
+                }
+
+                // Verify sig
+                // The reader has already made sure that the signature algorithm is supported,
+                // as we only support the Ed25519 algorithm, we know that it is necessarily this one.
+                if !self.verify_sig(&datas, sig_pubkey, user_msg_end) {
+                    return Err(IncomingMsgErr::InvalidHashOrSig.into());
+                }
+
+                // Update status
+                self.status
+                    .apply_action(Action::Receive(MsgType::Connect))?;
+
+                // Get peeer EPK and compute shared secret
+                self.peer_epk = Some(peer_ephemeral_pk.to_vec());
+                self.compute_shared_secret(&peer_ephemeral_pk[..])?;
+            }
+            MsgTypeHeaders::Ack { challenge } => {
+                // Verify challenge
+                if challenge != sha256(self.ephemeral_pubkey.as_ref()).as_ref() {
+                    return Err(IncomingMsgErr::InvalidChallenge.into());
+                }
+
+                let peer_sig_pubkey = if let Some(ref peer_sig_pubkey) = self.peer_sig_pubkey {
+                    peer_sig_pubkey
+                } else if self.ack_msg_recv_too_early.is_none() {
+                    self.ack_msg_recv_too_early = Some(incoming_datas.to_vec());
+                    return Ok(None);
+                } else {
+                    self.status = SecureLayerStatus::Fail;
+                    return Err(IncomingMsgErr::UnexpectedAckMsg.into());
+                };
+
+                // Verify sig
+                // The reader has already made sure that the signature algorithm is supported,
+                // as we only support the Ed25519 algorithm, we know that it is necessarily this one.
+                if !self.verify_sig(&datas, peer_sig_pubkey, user_msg_end) {
+                    return Err(IncomingMsgErr::InvalidHashOrSig.into());
+                }
+
+                // Update status
+                self.status.apply_action(Action::Receive(MsgType::Ack))?;
+            }
+            MsgTypeHeaders::UserMsg => {
+                // Verify status
+                if let Some(ActionSideEffects::PushUserMsgIntoTmpStack) = self
+                    .status
+                    .apply_action(Action::Receive(MsgType::UserMsg))?
+                {
+                    self.tmp_stack_user_msgs.push(datas);
+                    return Ok(None);
+                }
+
+                // Verify hash
+                let datas_hashed = &datas[..user_msg_end];
+                let hash = &datas[user_msg_end..];
+                if hash != sha256(datas_hashed).as_ref() {
+                    return Err(IncomingMsgErr::InvalidHashOrSig.into());
+                }
+            }
+        }
+
+        // Get message
+        let message = Message::from_bytes(
+            datas.drain(user_msg_begin..user_msg_end).collect(),
+            msg_type_headers,
+        )?;
+
+        Ok(Some(message))
+    }
+    /// Encrypt and write message on a writer
+    #[inline]
+    fn encrypt_and_write<W: Write>(
+        &mut self,
+        encapsuled_message: &EncapsuledMessage,
+        writer: &mut BufWriter<W>,
+    ) -> Result<()> {
+        let encrypt_algo_with_secret =
+            if let Some(ref encrypt_algo_with_secret) = self.encrypt_algo_with_secret {
+                encrypt_algo_with_secret
+            } else {
+                panic!("Dev error: try to get encrypt_algo_with_secret before it's computed !")
+            };
+
+        let mut datas_will_encrypted = BufWriter::new(Vec::with_capacity(
+            encapsuled_message.as_ref().len() + HASH_SIZE,
+        ));
+
+        // Write encapsuled message
+        datas_will_encrypted
+            .write(encapsuled_message.as_ref())
+            .map_err(Error::WriteError)?;
+        // Write encapsuled message hash
+        datas_will_encrypted
+            .write(sha256(encapsuled_message.as_ref()).as_ref())
+            .map_err(Error::WriteError)?;
+
+        // Flush datas_will_encrypted buffer
+        let datas_will_encrypted = datas_will_encrypted
+            .into_inner()
+            .map_err(|_| Error::BufferFlushError)?;
+
+        // Encrypt
+        encrypt(
+            &mut BufReader::new(&datas_will_encrypted[..]),
+            encrypt_algo_with_secret,
+            writer,
+        )?;
+
+        Ok(())
+    }
+    #[inline]
+    /// Create connect message
+    pub fn create_connect_message(
+        &mut self,
+        public_key: &[u8],
+        custom_datas: Option<&[u8]>,
+    ) -> Result<Vec<u8>> {
+        // Update status
+        self.status.apply_action(Action::Create(MsgType::Connect))?;
+
+        // Create message and update status
+        match self.encapsulate_message(&MessageRef::Connect {
+            sig_algo: SIG_ALGO_ED25519_ARRAY,
+            sig_pubkey: public_key.to_vec(),
+            custom_datas,
+        }) {
+            Ok(encapsuled_msg) => Ok(encapsuled_msg.datas),
+            Err(e) => {
+                self.status = SecureLayerStatus::Fail;
+                Err(e)
+            }
+        }
+    }
+    #[inline]
+    /// Create ack message
+    pub fn create_ack_message(&mut self, custom_datas: Option<&[u8]>) -> Result<Vec<u8>> {
+        // Update status
+        self.status.apply_action(Action::Create(MsgType::Ack))?;
+
+        // Create message and update status
+        match self.encapsulate_message(&MessageRef::Ack { custom_datas }) {
+            Ok(encapsuled_msg) => Ok(encapsuled_msg.datas),
+            Err(e) => {
+                self.status = SecureLayerStatus::Fail;
+                Err(e)
+            }
+        }
+    }
+    #[inline]
+    /// Write message
+    pub fn write_message<W: Write>(
+        &mut self,
+        datas: &[u8],
+        writer: &mut BufWriter<W>,
+    ) -> Result<()> {
+        // Update status
+        self.status.apply_action(Action::Create(MsgType::UserMsg))?;
+
+        match self.encapsuled_and_encrypt_and_write_message(datas, writer) {
+            Ok(()) => {
+                self.status = SecureLayerStatus::NegotiationSuccessful;
+                Ok(())
+            }
+            Err(e) => {
+                self.status = SecureLayerStatus::Fail;
+                Err(e)
+            }
+        }
+    }
+    #[inline]
+    fn encapsuled_and_encrypt_and_write_message<W: Write>(
+        &mut self,
+        datas: &[u8],
+        writer: &mut BufWriter<W>,
+    ) -> Result<()> {
+        let encapsuled_msg = self.encapsulate_message(&MessageRef::Message {
+            custom_datas: Some(datas),
+        })?;
+        self.encrypt_and_write(&encapsuled_msg, writer)
+    }
+    #[inline]
+    fn verify_sig(&self, datas: &[u8], sig_pubkey: &[u8], user_msg_end: usize) -> bool {
+        let datas_signed = &datas[..user_msg_end];
+        let sig = &datas[user_msg_end..];
+        signature::verify_sig(sig_pubkey, datas_signed, sig)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use crate::encryption::EncryptAlgo;
+    use crate::signature::SIG_ALGO_ED25519;
+    use crate::Seed32;
+    use ring::signature::{Ed25519KeyPair, KeyPair};
+
+    fn create_connect_msg_bytes(mut epk: Vec<u8>, sig_kp: &Ed25519KeyPair) -> Result<Vec<u8>> {
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut MAGIC_VALUE.to_vec());
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 74u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 1]); // CONNECT type
+        incoming_datas.append(&mut epk); // EPK
+        incoming_datas.append(&mut SIG_ALGO_ED25519.to_vec()); // SIG_ALGO
+        incoming_datas.append(&mut sig_kp.public_key().as_ref().to_vec()); // SIG_PK
+        incoming_datas.append(&mut vec![5, 4, 4, 5]); // User custom datas
+        let sig = sig_kp.sign(&incoming_datas);
+        incoming_datas.append(&mut sig.as_ref().to_vec()); // SIG
+        Ok(incoming_datas)
+    }
+
+    fn create_ack_msg_bytes(remote_epk: Vec<u8>, sig_kp: &Ed25519KeyPair) -> Result<Vec<u8>> {
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut MAGIC_VALUE.to_vec());
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 34u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 2]); // ACK type
+        incoming_datas.append(&mut sha256(&remote_epk).as_ref().to_vec()); // Challenge
+        let sig = sig_kp.sign(&incoming_datas);
+        incoming_datas.append(&mut sig.as_ref().to_vec()); // SIG
+        Ok(incoming_datas)
+    }
+
+    #[test]
+    fn test_change_config() -> Result<()> {
+        let mut msl = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+        msl.change_config(SdtlMinimalConfig {
+            encrypt_algo: EncryptAlgo::Chacha20Poly1305Aead,
+        });
+        Ok(())
+    }
+
+    #[test]
+    fn test_compute_shared_secret_twice() -> Result<()> {
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+        let msl2 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        msl1.compute_shared_secret(msl2.ephemeral_pubkey.as_ref())?;
+        msl1.compute_shared_secret(msl2.ephemeral_pubkey.as_ref())?;
+        Ok(())
+    }
+
+    #[test]
+    fn test_status_update_to_fail() -> Result<()> {
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+        let fake_encrypted_incoming_datas = &[0, 0, 0, 0];
+        let result = msl1.read(fake_encrypted_incoming_datas);
+
+        assert_eq!(SecureLayerStatus::Fail, msl1.status);
+
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::UnexpectedMessage, e);
+        } else {
+            panic!("unexpected result")
+        }
+        Ok(())
+    }
+
+    #[test]
+    fn test_ack_msg_with_wrong_challenge() -> Result<()> {
+        // Create ack message
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut MAGIC_VALUE.to_vec());
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 34u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 2]); // ACK type
+        incoming_datas.append(&mut [0u8; 32].to_vec()); // fake challenge
+        incoming_datas.append(&mut [0u8; 32].to_vec()); // fake sig
+
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Read ack msg
+        let result = msl1.read(&incoming_datas[..]);
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::InvalidChallenge, e);
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_write_user_msg_before_nego() -> Result<()> {
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Try to create ack message before connect message
+        let result = msl1.write_message(&[], &mut BufWriter::new(Vec::new()));
+        if let Err(Error::NegoMustHaveBeenSuccessful) = result {
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_create_ack_msg_before_connect() -> Result<()> {
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Try to create ack message before connect message
+        let result = msl1.create_ack_message(None);
+        if let Err(Error::ForbidWriteAckMsgNow) = result {
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_connect_msg_twice() -> Result<()> {
+        // Create sig keypair
+        let sig_kp = Ed25519KeyPair::from_seed_unchecked(Seed32::random().as_ref())
+            .map_err(|_| Error::FailtoGenSigKeyPair)?;
+
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        let _ = msl1.create_connect_message(sig_kp.public_key().as_ref(), None)?;
+
+        // Try to create connect message twice
+        let result = msl1.create_connect_message(sig_kp.public_key().as_ref(), None);
+        if let Err(Error::ConnectMsgAlreadyWritten) = result {
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_connect_msg_with_wrong_sig() -> Result<()> {
+        // Crate fake keys
+        let fake_ephem_pk = &[0u8; 32][..];
+        let fake_sig_pk = [0u8; 32].to_vec();
+        let _fake_signature_opt = Some(&[0u8; 32][..]);
+
+        // Create connect msg bytes
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut MAGIC_VALUE.to_vec());
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 74u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 1]); // CONNECT type
+        incoming_datas.append(&mut fake_ephem_pk.to_vec()); // EPK
+        incoming_datas.append(&mut SIG_ALGO_ED25519.to_vec()); // SIG_ALGO
+        incoming_datas.append(&mut fake_sig_pk.clone()); // SIG_PK
+        incoming_datas.append(&mut vec![5, 4, 4, 5]); // User custom datas
+        incoming_datas.append(&mut [0u8; 32].to_vec()); // fake sig
+
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Read connect msg
+        let result = msl1.read(&incoming_datas[..]);
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::InvalidHashOrSig, e);
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_recv_connect_msg_twice() -> Result<()> {
+        // Create sig keypair
+        let sig_kp = Ed25519KeyPair::from_seed_unchecked(Seed32::random().as_ref())
+            .map_err(|_| Error::FailtoGenSigKeyPair)?;
+
+        // Create EKP
+        let ephemeral_kp = EphemeralKeyPair::generate()?;
+
+        // Create connect msg bytes
+        let incoming_datas =
+            create_connect_msg_bytes(ephemeral_kp.public_key().as_ref().to_vec(), &sig_kp)?;
+
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Read connect message
+        let _ = msl1.read(&incoming_datas[..])?;
+
+        // Reread same connect message
+        let result = msl1.read(&incoming_datas[..]);
+        if let Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedConnectMsg)) = result {
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_recv_ack_msg_early_twice() -> Result<()> {
+        // Create sig keypair
+        let sig_kp = Ed25519KeyPair::from_seed_unchecked(Seed32::random().as_ref())
+            .map_err(|_| Error::FailtoGenSigKeyPair)?;
+
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Create ack msg bytes
+        let incoming_datas =
+            create_ack_msg_bytes(msl1.ephemeral_pubkey.as_ref().to_vec(), &sig_kp)?;
+
+        // Read ack message received too early
+        let _ = msl1.read(&incoming_datas[..]);
+        // Re-read ack message received too early
+        let result = msl1.read(&incoming_datas[..]);
+        if let Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedAckMsg)) = result {
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn test_recv_user_msg_before_nego() -> Result<()> {
+        // Create secure layer
+        let mut msl1 = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+        // Create empty user msg fakely encryted
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut vec![0, 0, 0, 0]);
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 2u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 0]); // USER_MSG_TYPE
+        incoming_datas.append(&mut sha256(&incoming_datas).as_ref().to_vec()); // Hash
+
+        // Read user message received before_nego
+        let result = msl1.read(&incoming_datas[..]);
+        if let Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedMessage)) = result {
+            Ok(())
+        } else {
+            println!("unexpected result={:?}", result);
+            panic!();
+        }
+    }
+
+}
diff --git a/lib/tools/pkstl/src/reader.rs b/lib/tools/pkstl/src/reader.rs
new file mode 100644
index 00000000..62dae03c
--- /dev/null
+++ b/lib/tools/pkstl/src/reader.rs
@@ -0,0 +1,373 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Define PKSTL reader.
+
+use crate::constants::*;
+use crate::encryption::{decrypt, EncryptAlgoWithSecretKey};
+use crate::errors::IncomingMsgErr;
+use crate::message::MsgTypeHeaders;
+use crate::signature::SIG_ALGO_ED25519;
+use crate::{Error, Result};
+use std::io::{BufWriter, Write};
+
+const MAGIC_VALUE_END: usize = 4;
+const VERSION_END: usize = 8;
+pub(crate) const ENCAPSULED_MSG_BEGIN: usize = 16;
+
+#[derive(Debug, PartialEq)]
+pub(crate) struct DecryptedIncomingDatas {
+    pub(crate) datas: Vec<u8>,
+    pub(crate) user_msg_begin: usize,
+    pub(crate) user_msg_end: usize,
+    pub(crate) msg_type_headers: MsgTypeHeaders,
+}
+
+/// Read incoming datas
+pub(crate) fn read(
+    encrypt_algo_with_secret_opt: Option<&EncryptAlgoWithSecretKey>,
+    incoming_datas: &[u8],
+    check_encrypt_state: bool,
+) -> std::result::Result<DecryptedIncomingDatas, Error> {
+    // Decrypt datas
+    let datas_encrypted;
+    let mut buffer = BufWriter::new(Vec::with_capacity(incoming_datas.len()));
+    if incoming_datas[..MAGIC_VALUE_END] == MAGIC_VALUE {
+        // Datas are not encrypted
+        datas_encrypted = false;
+        buffer.write(incoming_datas).map_err(Error::WriteError)?;
+    } else {
+        // Datas are encrypted
+        datas_encrypted = true;
+        if let Some(encrypt_algo_with_secret) = encrypt_algo_with_secret_opt {
+            decrypt(incoming_datas, encrypt_algo_with_secret, &mut buffer)?;
+        } else {
+            return Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedMessage));
+        }
+    }
+    let decrypted_datas = buffer.into_inner().map_err(|_| Error::BufferFlushError)?;
+
+    // Check magic value
+    if decrypted_datas[..MAGIC_VALUE_END] != MAGIC_VALUE {
+        return Err(IncomingMsgErr::InvalidMagicValue.into());
+    }
+
+    // Check version
+    if decrypted_datas[MAGIC_VALUE_END..VERSION_END] != CURRENT_VERSION {
+        return Err(IncomingMsgErr::UnsupportedVersion.into());
+    }
+
+    // Read ENCAPSULED_MSG_SIZE
+    let mut buffer_8_bytes: [u8; 8] = <[u8; 8]>::default();
+    buffer_8_bytes.copy_from_slice(&decrypted_datas[VERSION_END..ENCAPSULED_MSG_BEGIN]);
+    let encapsuled_msg_size = u64::from_be_bytes(buffer_8_bytes) as usize;
+    let user_msg_end = ENCAPSULED_MSG_BEGIN + encapsuled_msg_size;
+
+    // Read type headers
+    let (msg_type_headers, type_headers_len) =
+        read_type_headers(&decrypted_datas[ENCAPSULED_MSG_BEGIN..])?;
+
+    if check_encrypt_state && datas_encrypted != msg_type_headers.must_be_encrypted() {
+        Err(Error::RecvInvalidMsg(
+            IncomingMsgErr::UnexpectedEncryptionState,
+        ))
+    } else {
+        Ok(DecryptedIncomingDatas {
+            datas: decrypted_datas,
+            user_msg_begin: ENCAPSULED_MSG_BEGIN + type_headers_len,
+            user_msg_end,
+            msg_type_headers,
+        })
+    }
+}
+
+fn read_type_headers(type_headers: &[u8]) -> Result<(MsgTypeHeaders, usize)> {
+    // Match message type
+    match &type_headers[..MSG_TYPE_LEN] {
+        USER_MSG_TYPE => Ok((MsgTypeHeaders::UserMsg, MSG_TYPE_LEN)),
+        CONNECT_MSG_TYPE => {
+            // Read PEER_EPHEMERAL_PUBKEY
+            let mut peer_ephemeral_pk = [0u8; EPK_SIZE];
+            peer_ephemeral_pk.copy_from_slice(&type_headers[MSG_TYPE_LEN..MSG_TYPE_LEN + EPK_SIZE]);
+            // Read SIG_ALGO and SIG_PUBKEY
+            match &type_headers[(MSG_TYPE_LEN + EPK_SIZE)..(SIG_PUBKEY_BEGIN)] {
+                SIG_ALGO_ED25519 => {
+                    let mut sig_algo = [0u8; SIG_ALGO_LEN];
+                    sig_algo.copy_from_slice(
+                        &type_headers
+                            [(MSG_TYPE_LEN + EPK_SIZE)..(MSG_TYPE_LEN + EPK_SIZE + SIG_ALGO_LEN)],
+                    );
+                    Ok((
+                        MsgTypeHeaders::Connect {
+                            peer_ephemeral_pk,
+                            sig_algo,
+                            sig_pubkey: type_headers[SIG_PUBKEY_BEGIN..(SIG_PUBKEY_BEGIN + 32)]
+                                .to_vec(),
+                        },
+                        SIG_PUBKEY_BEGIN + 32,
+                    ))
+                }
+                _ => Err(IncomingMsgErr::UnsupportedSigAlgo.into()),
+            }
+        }
+        ACK_MSG_TYPE => {
+            let mut challenge = [0u8; CHALLENGE_SIZE];
+            challenge
+                .copy_from_slice(&&type_headers[MSG_TYPE_LEN..(MSG_TYPE_LEN + CHALLENGE_SIZE)]);
+            Ok((
+                MsgTypeHeaders::Ack { challenge },
+                MSG_TYPE_LEN + CHALLENGE_SIZE,
+            ))
+        }
+        _ => Err(IncomingMsgErr::UnknownMessageType.into()),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use crate::digest::sha256;
+    use crate::encryption::{encrypt, tests::gen_random_encrypt_algo_with_secret};
+    use crate::signature::{SIG_ALGO_ED25519, SIG_ALGO_ED25519_ARRAY};
+    use pretty_assertions::assert_eq;
+    use std::io::BufReader;
+
+    #[test]
+    fn test_unexpected_user_msg() {
+        let fake_encrypted_incoming_datas = &[0, 0, 0, 0];
+        let result = read(None, fake_encrypted_incoming_datas, true);
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::UnexpectedMessage, e);
+        } else {
+            panic!("unexpected result")
+        }
+    }
+
+    #[test]
+    fn test_msg_with_unsupported_version() {
+        let mut fake_incoming_datas = MAGIC_VALUE.to_vec();
+        fake_incoming_datas.append(&mut vec![0, 0, 0, 2]);
+
+        let result = read(None, &fake_incoming_datas, true);
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::UnsupportedVersion, e);
+        } else {
+            panic!("unexpected result")
+        }
+    }
+
+    #[test]
+    fn test_unencrypted_usr_msg() {
+        let mut empty_user_msg = MAGIC_VALUE.to_vec();
+        empty_user_msg.append(&mut CURRENT_VERSION.to_vec());
+        empty_user_msg.append(&mut vec![0, 0, 0, 0, 0, 0, 0, 2]); // ENCAPSULED_MSG_SIZE
+        empty_user_msg.append(&mut USER_MSG_TYPE.to_vec());
+
+        let result = read(None, &empty_user_msg, true);
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::UnexpectedEncryptionState, e);
+        } else {
+            panic!("unexpected result")
+        }
+    }
+
+    #[test]
+    fn test_user_msg_with_wrong_magiv_value() -> Result<()> {
+        let wrong_magic_value = vec![0, 0, 0, 0];
+        let encrypt_algo_with_secret = gen_random_encrypt_algo_with_secret();
+        let mut encrypted_datas = BufWriter::new(Vec::new());
+
+        encrypt(
+            &mut BufReader::new(&wrong_magic_value[..]),
+            &encrypt_algo_with_secret,
+            &mut encrypted_datas,
+        )?;
+        let encrypted_incoming_datas = encrypted_datas.into_inner().expect("buffer flush error");
+
+        let result = read(
+            Some(&encrypt_algo_with_secret),
+            &encrypted_incoming_datas[..],
+            true,
+        );
+        if let Err(Error::RecvInvalidMsg(e)) = result {
+            assert_eq!(IncomingMsgErr::InvalidMagicValue, e);
+        } else {
+            panic!("unexpected result")
+        }
+        Ok(())
+    }
+
+    #[test]
+    fn test_read() -> Result<()> {
+        // Crate fake keys
+        let fake_ephem_pk = &[0u8; 32][..];
+        let fake_sig_pk = [0u8; 32].to_vec();
+        let _fake_signature_opt = Some(&[0u8; 32][..]);
+
+        // Create fake challenge
+        let mut fake_challenge = [0u8; 32];
+        fake_challenge.copy_from_slice(sha256(fake_ephem_pk).as_ref());
+
+        // Create encrypt_algo_with_secret
+        let encrypt_algo_with_secret = gen_random_encrypt_algo_with_secret();
+
+        /////////////////////
+        // CONNECT MSG
+        /////////////////////
+
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut MAGIC_VALUE.to_vec());
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 74u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 1]); // CONNECT type
+        incoming_datas.append(&mut fake_ephem_pk.to_vec()); // EPK
+        incoming_datas.append(&mut SIG_ALGO_ED25519.to_vec()); // SIG_ALGO
+        incoming_datas.append(&mut fake_sig_pk.clone()); // SIG_PK
+        incoming_datas.append(&mut vec![5, 4, 4, 5]); // User custom datas
+        incoming_datas.append(&mut [0u8; 32].to_vec()); // fake sig
+        assert_eq!(
+            DecryptedIncomingDatas {
+                datas: incoming_datas.clone(),
+                user_msg_begin: 86,
+                user_msg_end: 90,
+                msg_type_headers: MsgTypeHeaders::Connect {
+                    peer_ephemeral_pk: [0u8; EPK_SIZE],
+                    sig_algo: SIG_ALGO_ED25519_ARRAY,
+                    sig_pubkey: fake_sig_pk,
+                }
+            },
+            read(Some(&encrypt_algo_with_secret), &incoming_datas[..], true)?,
+        );
+
+        /////////////////////
+        // ACK MSG
+        /////////////////////
+
+        // Read incoming ack message without custom datas
+        let mut incoming_datas = Vec::with_capacity(100);
+        incoming_datas.append(&mut MAGIC_VALUE.to_vec());
+        incoming_datas.append(&mut CURRENT_VERSION.to_vec());
+        incoming_datas.append(&mut 34u64.to_be_bytes().to_vec()); // Encapsuled message length
+        incoming_datas.append(&mut vec![0, 2]); // ACK type
+        incoming_datas.append(&mut fake_challenge.to_vec()); // ACK challenge
+        incoming_datas.append(&mut [0u8; 32].to_vec()); // fake sig
+        assert_eq!(
+            DecryptedIncomingDatas {
+                datas: incoming_datas.clone(),
+                user_msg_begin: 50,
+                user_msg_end: 50,
+                msg_type_headers: MsgTypeHeaders::Ack {
+                    challenge: fake_challenge,
+                }
+            },
+            read(Some(&encrypt_algo_with_secret), &incoming_datas[..], true)?,
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_read_user_type_headers() -> Result<()> {
+        let type_headers = vec![0, 0]; // USER_MSG_TYPE
+
+        let expected = (MsgTypeHeaders::UserMsg, 2);
+
+        assert_eq!(expected, read_type_headers(&type_headers[..])?);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_read_unknown_type_headers() {
+        let type_headers = vec![1, 0]; // Unknown type
+
+        let result = read_type_headers(&type_headers[..]);
+
+        if let Err(Error::RecvInvalidMsg(e)) = read_type_headers(&type_headers[..]) {
+            assert_eq!(e, IncomingMsgErr::UnknownMessageType);
+        } else {
+            println!("{:?}", result);
+            panic!("Unexpected result");
+        }
+    }
+
+    #[test]
+    fn test_read_ack_type_headers() -> Result<()> {
+        let challenge = [
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+            9, 0, 1,
+        ];
+        let mut type_headers = vec![
+            0, 2, // ACK_MSG_TYPE
+        ];
+        type_headers.append(&mut challenge.to_vec());
+
+        let expected = (MsgTypeHeaders::Ack { challenge }, 34);
+
+        assert_eq!(expected, read_type_headers(&type_headers[..])?);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_read_connect_type_headers() -> Result<()> {
+        let type_headers = vec![
+            0, 1, // CONNECT_MSG_TYPE
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+            9, 0, 1, // EPK (32 bytes)
+            0, 0, 0, 0, // SIG_ALGO_ED25519
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+            9, 0, 1, // Sig pubkey (32 bytes)
+        ];
+
+        let expected = (
+            MsgTypeHeaders::Connect {
+                peer_ephemeral_pk: [
+                    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
+                    6, 7, 8, 9, 0, 1,
+                ],
+                sig_algo: [0, 0, 0, 0],
+                sig_pubkey: type_headers[38..].to_vec(),
+            },
+            70,
+        );
+
+        assert_eq!(expected, read_type_headers(&type_headers[..])?);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_read_connect_type_headers_with_unsupported_sig_algo() {
+        let type_headers = vec![
+            0, 1, // CONNECT_MSG_TYPE
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+            9, 0, 1, // EPK (32 bytes)
+            0, 0, 0, 1, // Unsupported sig algo
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+            9, 0, 1, // Sig pubkey (32 bytes)
+        ];
+
+        let result = read_type_headers(&type_headers[..]);
+
+        if let Err(Error::RecvInvalidMsg(e)) = read_type_headers(&type_headers[..]) {
+            assert_eq!(e, IncomingMsgErr::UnsupportedSigAlgo);
+        } else {
+            println!("{:?}", result);
+            panic!("Unexpected result");
+        }
+    }
+}
diff --git a/lib/tools/pkstl/src/seeds.rs b/lib/tools/pkstl/src/seeds.rs
new file mode 100644
index 00000000..f62ffed2
--- /dev/null
+++ b/lib/tools/pkstl/src/seeds.rs
@@ -0,0 +1,151 @@
+//  Copyright (C) 2017-2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Provide wrappers around cryptographic seeds
+
+use clear_on_drop::clear::Clear;
+
+/// Store a 32 bytes seed.
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct Seed32([u8; 32]);
+
+impl AsRef<[u8]> for Seed32 {
+    fn as_ref(&self) -> &[u8] {
+        &self.0
+    }
+}
+
+impl AsMut<[u8]> for Seed32 {
+    fn as_mut(&mut self) -> &mut [u8] {
+        &mut self.0
+    }
+}
+
+impl Drop for Seed32 {
+    #[inline]
+    fn drop(&mut self) {
+        <[u8; 32] as Clear>::clear(&mut self.0);
+    }
+}
+
+impl Seed32 {
+    #[inline]
+    /// Create new seed
+    pub fn new(seed_bytes: [u8; 32]) -> Seed32 {
+        Seed32(seed_bytes)
+    }
+    #[inline]
+    /// Generate random seed
+    pub fn random() -> Seed32 {
+        if let Ok(random_bytes) = ring::rand::generate::<[u8; 32]>(&ring::rand::SystemRandom::new())
+        {
+            Seed32::new(random_bytes.expose())
+        } else {
+            panic!("System error: fail to generate random seed !")
+        }
+    }
+}
+
+/// Store a 48 bytes seed.
+#[derive(Default)]
+pub struct Seed48(InnerSeed48);
+
+struct InnerSeed48([u8; 48]);
+
+impl Default for InnerSeed48 {
+    fn default() -> Self {
+        InnerSeed48([0u8; 48])
+    }
+}
+
+impl AsRef<[u8]> for Seed48 {
+    fn as_ref(&self) -> &[u8] {
+        &(self.0).0
+    }
+}
+
+impl AsMut<[u8]> for Seed48 {
+    fn as_mut(&mut self) -> &mut [u8] {
+        &mut (self.0).0
+    }
+}
+
+impl Drop for Seed48 {
+    #[inline]
+    fn drop(&mut self) {
+        <InnerSeed48 as Clear>::clear(&mut self.0);
+    }
+}
+
+#[cfg(test)]
+impl Seed48 {
+    #[inline]
+    /// Create new seed
+    pub fn new(seed_bytes: [u8; 48]) -> Seed48 {
+        Seed48(InnerSeed48(seed_bytes))
+    }
+}
+
+/// Store a 64 bytes seed.
+#[derive(Default)]
+pub struct Seed64(InnerSeed64);
+
+struct InnerSeed64([u8; 64]);
+
+impl Default for InnerSeed64 {
+    fn default() -> Self {
+        InnerSeed64([0u8; 64])
+    }
+}
+
+impl AsMut<[u8]> for Seed64 {
+    fn as_mut(&mut self) -> &mut [u8] {
+        &mut (self.0).0
+    }
+}
+
+impl Drop for Seed64 {
+    #[inline]
+    fn drop(&mut self) {
+        <InnerSeed64 as Clear>::clear(&mut self.0);
+    }
+}
+
+#[cfg(test)]
+pub mod tests {
+
+    use super::*;
+
+    #[inline]
+    /// Generate random Seed48
+    pub fn random_seed_48() -> Seed48 {
+        if let Ok(random_bytes) = ring::rand::generate::<[u8; 48]>(&ring::rand::SystemRandom::new())
+        {
+            Seed48::new(random_bytes.expose())
+        } else {
+            panic!("System error: fail to generate random seed !")
+        }
+    }
+
+    #[test]
+    fn tests_seed32() {
+        assert_ne!(Seed32::random(), Seed32::random());
+
+        let mut seed = Seed32::new([3u8; 32]);
+
+        assert_eq!(&[3u8; 32], seed.as_ref());
+        assert_eq!(&mut [3u8; 32], seed.as_mut());
+    }
+}
diff --git a/lib/tools/pkstl/src/signature.rs b/lib/tools/pkstl/src/signature.rs
new file mode 100644
index 00000000..29641160
--- /dev/null
+++ b/lib/tools/pkstl/src/signature.rs
@@ -0,0 +1,30 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Define PKSTL Signature.
+
+use ring::signature::UnparsedPublicKey;
+
+/// Signature algorithm Ed25519
+pub const SIG_ALGO_ED25519: &[u8] = &[0, 0, 0, 0];
+
+/// Signature algorithm Ed25519 array
+pub const SIG_ALGO_ED25519_ARRAY: [u8; 4] = [0, 0, 0, 0];
+
+pub(crate) fn verify_sig(pubkey: &[u8], message: &[u8], sig: &[u8]) -> bool {
+    UnparsedPublicKey::new(&ring::signature::ED25519, pubkey)
+        .verify(message, sig)
+        .is_ok()
+}
diff --git a/lib/tools/pkstl/src/status.rs b/lib/tools/pkstl/src/status.rs
new file mode 100644
index 00000000..200ac43e
--- /dev/null
+++ b/lib/tools/pkstl/src/status.rs
@@ -0,0 +1,127 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Handle KPSTL status.
+
+use crate::errors::IncomingMsgErr;
+use crate::{Action, ActionSideEffects, Error, LocalNegoThread, MsgType, RemoteNegoThread, Result};
+
+/// Secure layer status
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub(crate) enum SecureLayerStatus {
+    /// An error has occurred or one peer message is wrong
+    Fail,
+    /// Negotiation in progress
+    OngoingNegotiation {
+        local: LocalNegoThread,
+        remote: RemoteNegoThread,
+    },
+    /// Equivalent to "AckMsgWrittenAndPeerAckMsgOk"
+    NegotiationSuccessful,
+}
+
+impl SecureLayerStatus {
+    pub(crate) fn init() -> Self {
+        SecureLayerStatus::OngoingNegotiation {
+            local: LocalNegoThread::Created,
+            remote: RemoteNegoThread::WaitConnectMsg,
+        }
+    }
+    pub(crate) fn apply_action(&mut self, action: Action) -> Result<Option<ActionSideEffects>> {
+        match self {
+            Self::Fail => Err(Error::ConnectionHadFail),
+            Self::OngoingNegotiation { local, remote } => match action {
+                Action::Create(msg_type) => match msg_type {
+                    MsgType::Connect => {
+                        if *local == LocalNegoThread::Created {
+                            *local = LocalNegoThread::ConnectMsgSent;
+                            Ok(None)
+                        } else {
+                            Err(Error::ConnectMsgAlreadyWritten)
+                        }
+                    }
+                    MsgType::Ack => {
+                        if *remote == RemoteNegoThread::ValidConnectMsgReceived {
+                            if *local == LocalNegoThread::ValidAckMsgReceived {
+                                *self = Self::NegotiationSuccessful;
+                            } else {
+                                *remote = RemoteNegoThread::AckMsgSent;
+                            }
+                            Ok(None)
+                        } else {
+                            Err(Error::ForbidWriteAckMsgNow)
+                        }
+                    }
+                    MsgType::UserMsg => {
+                        *self = Self::Fail;
+                        Err(Error::NegoMustHaveBeenSuccessful)
+                    }
+                },
+                Action::Receive(msg_type) => match msg_type {
+                    MsgType::Connect => {
+                        if *remote == RemoteNegoThread::WaitConnectMsg {
+                            *remote = RemoteNegoThread::ValidConnectMsgReceived;
+                            Ok(None)
+                        } else {
+                            *self = Self::Fail;
+                            Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedConnectMsg))
+                        }
+                    }
+                    MsgType::Ack => {
+                        if *local == LocalNegoThread::ConnectMsgSent {
+                            if *remote == RemoteNegoThread::AckMsgSent {
+                                *self = Self::NegotiationSuccessful;
+                            } else {
+                                *local = LocalNegoThread::ValidAckMsgReceived;
+                            }
+                            Ok(None)
+                        } else {
+                            *self = Self::Fail;
+                            Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedAckMsg))
+                        }
+                    }
+                    MsgType::UserMsg => {
+                        if *remote == RemoteNegoThread::AckMsgSent
+                            && *local == LocalNegoThread::ConnectMsgSent
+                        {
+                            Ok(Some(ActionSideEffects::PushUserMsgIntoTmpStack))
+                        } else {
+                            *self = Self::Fail;
+                            Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedMessage))
+                        }
+                    }
+                },
+            },
+            Self::NegotiationSuccessful => match action {
+                Action::Create(msg_type) => match msg_type {
+                    MsgType::Connect => Err(Error::ConnectMsgAlreadyWritten),
+                    MsgType::Ack => Err(Error::ForbidWriteAckMsgNow),
+                    MsgType::UserMsg => Ok(None),
+                },
+                Action::Receive(msg_type) => match msg_type {
+                    MsgType::Connect => {
+                        *self = Self::Fail;
+                        Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedConnectMsg))
+                    }
+                    MsgType::Ack => {
+                        *self = Self::Fail;
+                        Err(Error::RecvInvalidMsg(IncomingMsgErr::UnexpectedAckMsg))
+                    }
+                    MsgType::UserMsg => Ok(None),
+                },
+            },
+        }
+    }
+}
diff --git a/lib/tools/pkstl/tests/complete_mode.rs b/lib/tools/pkstl/tests/complete_mode.rs
new file mode 100644
index 00000000..40b1b0b5
--- /dev/null
+++ b/lib/tools/pkstl/tests/complete_mode.rs
@@ -0,0 +1,196 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Test Public Key Secure Transport Layer in complete mode.
+
+#[cfg(feature = "zip-sign")]
+mod tests {
+    use pkstl::*;
+    use ring::signature::{Ed25519KeyPair, KeyPair};
+    use std::io::BufWriter;
+
+    trait AsOptRef {
+        fn as_opt_ref(&self) -> Option<&[u8]>;
+    }
+
+    impl AsOptRef for Option<Vec<u8>> {
+        fn as_opt_ref(&self) -> Option<&[u8]> {
+            match self {
+                Some(ref datas) => Some(&datas[..]),
+                None => None,
+            }
+        }
+    }
+
+    fn server_infos() -> Result<(SecureLayer, Vec<u8>)> {
+        // Create server sig keypair seed
+        let seed = Seed32::random();
+
+        // Create server secure layer
+        let server_msl = SecureLayer::create(SdtlConfig::default(), Some(seed.clone()), None)?;
+
+        // Get server sig pubkey
+        let server_sig_pubkey = Ed25519KeyPair::from_seed_unchecked(seed.as_ref())
+            .map_err(|_| Error::FailtoGenSigKeyPair)?
+            .public_key()
+            .as_ref()
+            .to_vec();
+
+        Ok((server_msl, server_sig_pubkey))
+    }
+
+    fn client_infos(expected_server_sig_pubkey: Option<Vec<u8>>) -> Result<SecureLayer> {
+        // Create client secure layer
+        let client_msl =
+            SecureLayer::create(SdtlConfig::default(), None, expected_server_sig_pubkey)?;
+
+        Ok(client_msl)
+    }
+
+    fn send_connect_msg(
+        sender_msl: &mut SecureLayer,
+        receiver_msl: &mut SecureLayer,
+        custom_datas: Option<Vec<u8>>,
+    ) -> Result<Vec<u8>> {
+        // Write connect message and it's sig in channel
+        let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+        sender_msl.write_connect_msg_bin(custom_datas.as_opt_ref(), &mut channel)?;
+
+        // Receiver read connect message from channel
+        let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+        let msg_received = receiver_msl
+            .read_bin(&channel[..])?
+            .expect("Must be receive a message");
+        if let IncomingBinaryMessage::Connect {
+            custom_datas: custom_datas_received,
+            peer_sig_public_key,
+        } = msg_received
+        {
+            assert_eq!(custom_datas, custom_datas_received);
+            Ok(peer_sig_public_key)
+        } else {
+            print!("Unexpected incoming message={:?}", msg_received);
+            panic!();
+        }
+    }
+
+    fn send_ack_msg(
+        sender_msl: &mut SecureLayer,
+        receiver_msl: &mut SecureLayer,
+        custom_datas: Option<Vec<u8>>,
+    ) -> Result<()> {
+        // Write ack message and it's sig in channel
+        let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+        sender_msl.write_ack_msg_bin(custom_datas.as_opt_ref(), &mut channel)?;
+
+        // Receiver read ack message from channel
+        let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+        let msg_received = receiver_msl
+            .read_bin(&channel[..])?
+            .expect("Must be receive a message");
+        if let IncomingBinaryMessage::Ack {
+            custom_datas: custom_datas_received,
+        } = msg_received
+        {
+            assert_eq!(custom_datas, custom_datas_received);
+            Ok(())
+        } else {
+            print!("Unexpected incoming message={:?}", msg_received);
+            panic!();
+        }
+    }
+
+    fn send_user_msg(
+        sender_msl: &mut SecureLayer,
+        receiver_msl: &mut SecureLayer,
+        datas: Vec<u8>,
+    ) -> Result<()> {
+        // Write user message and it's sig in channel
+        let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+        sender_msl.write_bin(&datas[..], &mut channel)?;
+
+        // Receiver read user message from channel
+        let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+        let msg_received = receiver_msl
+            .read_bin(&channel[..])?
+            .expect("Must be receive a message");
+        if let IncomingBinaryMessage::Message {
+            datas: datas_received,
+        } = msg_received
+        {
+            assert_eq!(Some(datas), datas_received);
+            Ok(())
+        } else {
+            print!("Unexpected incoming message={:?}", msg_received);
+            panic!();
+        }
+    }
+
+    #[test]
+    fn ordered_passing_case() -> Result<()> {
+        //////////////////////////
+        // SERVER INFOS
+        //////////////////////////
+
+        let (mut server_msl, server_sig_pk) = server_infos()?;
+
+        //////////////////////////
+        // CLIENT INFOS
+        //////////////////////////
+
+        let mut client_msl = client_infos(Some(server_sig_pk.clone()))?;
+
+        //////////////////////////
+        // CLIENT CONNECT MSG
+        //////////////////////////
+
+        let _client_sig_pk_recv =
+            send_connect_msg(&mut client_msl, &mut server_msl, Some(vec![5, 4, 4, 5]))?;
+
+        //////////////////////////
+        // SERVER CONNECT MSG
+        //////////////////////////
+
+        let server_sig_pk_recv =
+            send_connect_msg(&mut server_msl, &mut client_msl, Some(vec![5, 3, 3, 5]))?;
+        assert_eq!(server_sig_pk, server_sig_pk_recv);
+
+        //////////////////////////
+        // SERVER ACK MSG
+        //////////////////////////
+
+        send_ack_msg(&mut server_msl, &mut client_msl, Some(vec![5, 9, 9, 5]))?;
+
+        //////////////////////////
+        // CLIENT ACK MSG
+        //////////////////////////
+
+        send_ack_msg(&mut client_msl, &mut server_msl, Some(vec![5, 0, 0, 5]))?;
+
+        //////////////////////////
+        // CLIENT USER MSG
+        //////////////////////////
+
+        send_user_msg(&mut client_msl, &mut server_msl, vec![5, 5, 5, 5])?;
+
+        //////////////////////////
+        // SERVER USER MSG
+        //////////////////////////
+
+        send_user_msg(&mut server_msl, &mut client_msl, vec![9, 9, 9, 9])?;
+
+        Ok(())
+    }
+}
diff --git a/lib/tools/pkstl/tests/complete_serde_mode.rs b/lib/tools/pkstl/tests/complete_serde_mode.rs
new file mode 100644
index 00000000..973958a3
--- /dev/null
+++ b/lib/tools/pkstl/tests/complete_serde_mode.rs
@@ -0,0 +1,236 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Test Public Key Secure Transport Layer in complete mode with "ser" feature.
+
+#[cfg(feature = "ser")]
+mod tests {
+    use pkstl::*;
+    use ring::signature::{Ed25519KeyPair, KeyPair};
+    use serde::de::DeserializeOwned;
+    use serde::Serialize;
+    use std::fmt::Debug;
+    use std::io::BufWriter;
+
+    fn server_infos(format: MessageFormat) -> Result<(SecureLayer, Vec<u8>)> {
+        // Create server sig keypair seed
+        let seed = Seed32::random();
+
+        // Create server secure layer
+        let mut conf = SdtlConfig::default();
+        conf.message_format = format;
+        let server_msl = SecureLayer::create(conf, Some(seed.clone()), None)?;
+
+        // Get server sig pubkey
+        let server_sig_pubkey = Ed25519KeyPair::from_seed_unchecked(seed.as_ref())
+            .map_err(|_| Error::FailtoGenSigKeyPair)?
+            .public_key()
+            .as_ref()
+            .to_vec();
+
+        Ok((server_msl, server_sig_pubkey))
+    }
+
+    fn client_infos(
+        expected_server_sig_pubkey: Option<Vec<u8>>,
+        format: MessageFormat,
+    ) -> Result<SecureLayer> {
+        // Create client secure layer
+        let mut conf = SdtlConfig::default();
+        conf.message_format = format;
+        let client_msl = SecureLayer::create(conf, None, expected_server_sig_pubkey)?;
+
+        Ok(client_msl)
+    }
+
+    fn send_connect_msg<D: Debug + PartialEq + Serialize + DeserializeOwned>(
+        sender_msl: &mut SecureLayer,
+        receiver_msl: &mut SecureLayer,
+        custom_datas: Option<D>,
+    ) -> Result<Vec<u8>> {
+        // Write connect message and it's sig in channel
+        let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+        sender_msl.write_connect_msg(custom_datas.as_ref(), &mut channel)?;
+
+        // Receiver read connect message from channel
+        let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+        let msg_received = receiver_msl
+            .read(&channel[..])?
+            .expect("Must be receive a message");
+        if let IncomingMessage::Connect {
+            custom_datas: custom_datas_received,
+            peer_sig_public_key,
+        } = msg_received
+        {
+            assert_eq!(custom_datas, custom_datas_received);
+            Ok(peer_sig_public_key)
+        } else {
+            print!("Unexpected incoming message={:?}", msg_received);
+            panic!();
+        }
+    }
+
+    fn send_ack_msg<D: Debug + PartialEq + Serialize + DeserializeOwned>(
+        sender_msl: &mut SecureLayer,
+        receiver_msl: &mut SecureLayer,
+        custom_datas: Option<D>,
+    ) -> Result<()> {
+        // Write ack message and it's sig in channel
+        let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+        sender_msl.write_ack_msg(custom_datas.as_ref(), &mut channel)?;
+
+        // Receiver read ack message from channel
+        let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+        let msg_received = receiver_msl
+            .read(&channel[..])?
+            .expect("Must be receive a message");
+        if let IncomingMessage::Ack {
+            custom_datas: custom_datas_received,
+        } = msg_received
+        {
+            assert_eq!(custom_datas, custom_datas_received);
+            Ok(())
+        } else {
+            print!("Unexpected incoming message={:?}", msg_received);
+            panic!();
+        }
+    }
+
+    fn send_user_msg<D: Debug + PartialEq + Serialize + DeserializeOwned>(
+        sender_msl: &mut SecureLayer,
+        receiver_msl: &mut SecureLayer,
+        datas: D,
+    ) -> Result<()> {
+        // Write user message and it's sig in channel
+        let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+        sender_msl.write(&datas, &mut channel)?;
+
+        // Receiver read user message from channel
+        let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+        let msg_received = receiver_msl
+            .read(&channel[..])?
+            .expect("Must be receive a message");
+        if let IncomingMessage::Message {
+            datas: datas_received,
+        } = msg_received
+        {
+            assert_eq!(Some(datas), datas_received);
+            Ok(())
+        } else {
+            print!("Unexpected incoming message={:?}", msg_received);
+            panic!();
+        }
+    }
+
+    #[cfg(feature = "bin")]
+    #[test]
+    fn ordered_passing_case_bincode() -> Result<()> {
+        test_ordered_passing_case(
+            MessageFormat::Bincode,
+            Some("abc".to_owned()),
+            None,
+            Some("blablabla".to_owned()),
+        )
+    }
+
+    #[cfg(feature = "cbor")]
+    #[test]
+    fn ordered_passing_case_cbor() -> Result<()> {
+        test_ordered_passing_case(
+            MessageFormat::Cbor,
+            None,
+            Some("def".to_owned()),
+            Some("blablabla".to_owned()),
+        )
+    }
+
+    #[cfg(feature = "json")]
+    #[test]
+    fn ordered_passing_case_json() -> Result<()> {
+        test_ordered_passing_case(
+            MessageFormat::Utf8Json,
+            Some("abc".to_owned()),
+            Some("def".to_owned()),
+            None,
+        )
+    }
+
+    fn test_ordered_passing_case<D: Clone + Debug + PartialEq + Serialize + DeserializeOwned>(
+        message_format: MessageFormat,
+        connect_msg_custom_datas: Option<D>,
+        ack_msg_custom_datas: Option<D>,
+        user_msg_datas: Option<D>,
+    ) -> Result<()> {
+        //////////////////////////
+        // SERVER INFOS
+        //////////////////////////
+
+        let (mut server_msl, server_sig_pk) = server_infos(message_format)?;
+
+        //////////////////////////
+        // CLIENT INFOS
+        //////////////////////////
+
+        let mut client_msl = client_infos(Some(server_sig_pk.clone()), message_format)?;
+
+        //////////////////////////
+        // CLIENT CONNECT MSG
+        //////////////////////////
+
+        let _client_sig_pk_recv = send_connect_msg(
+            &mut client_msl,
+            &mut server_msl,
+            connect_msg_custom_datas.clone(),
+        )?;
+
+        //////////////////////////
+        // SERVER CONNECT MSG
+        //////////////////////////
+
+        let server_sig_pk_recv =
+            send_connect_msg(&mut server_msl, &mut client_msl, connect_msg_custom_datas)?;
+        assert_eq!(server_sig_pk, server_sig_pk_recv);
+
+        //////////////////////////
+        // SERVER ACK MSG
+        //////////////////////////
+
+        send_ack_msg(
+            &mut server_msl,
+            &mut client_msl,
+            ack_msg_custom_datas.clone(),
+        )?;
+
+        //////////////////////////
+        // CLIENT ACK MSG
+        //////////////////////////
+
+        send_ack_msg(&mut client_msl, &mut server_msl, ack_msg_custom_datas)?;
+
+        //////////////////////////
+        // CLIENT USER MSG
+        //////////////////////////
+
+        send_user_msg(&mut client_msl, &mut server_msl, user_msg_datas.clone())?;
+
+        //////////////////////////
+        // SERVER USER MSG
+        //////////////////////////
+
+        send_user_msg(&mut server_msl, &mut client_msl, user_msg_datas)?;
+
+        Ok(())
+    }
+}
diff --git a/lib/tools/pkstl/tests/minimal_mode.rs b/lib/tools/pkstl/tests/minimal_mode.rs
new file mode 100644
index 00000000..de5bd53d
--- /dev/null
+++ b/lib/tools/pkstl/tests/minimal_mode.rs
@@ -0,0 +1,472 @@
+//  Copyright (C) 2019  Eloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Test Public Key Secure Transport Layer in minimal mode.
+
+use pkstl::*;
+use ring::signature::{Ed25519KeyPair, KeyPair};
+use std::io::{BufWriter, Write};
+
+trait AsOptRef {
+    fn as_opt_ref(&self) -> Option<&[u8]>;
+}
+
+impl AsOptRef for Option<Vec<u8>> {
+    fn as_opt_ref(&self) -> Option<&[u8]> {
+        match self {
+            Some(ref datas) => Some(&datas[..]),
+            None => None,
+        }
+    }
+}
+
+fn client_infos(server_sig_kp: &[u8]) -> Result<(MinimalSecureLayer, Ed25519KeyPair)> {
+    // Create client sig keypair
+    let client_sig_kp = Ed25519KeyPair::from_seed_unchecked(Seed32::random().as_ref())
+        .map_err(|_| Error::FailtoGenSigKeyPair)?;
+
+    // Create client secure layer
+    let client_msl =
+        MinimalSecureLayer::create(SdtlMinimalConfig::default(), Some(server_sig_kp.to_vec()))?;
+
+    Ok((client_msl, client_sig_kp))
+}
+
+fn server_infos() -> Result<(MinimalSecureLayer, Ed25519KeyPair)> {
+    // Create server secure layer
+    let server_msl = MinimalSecureLayer::create(SdtlMinimalConfig::default(), None)?;
+
+    // Create server sig keypair
+    let server_sig_kp = Ed25519KeyPair::from_seed_unchecked(Seed32::random().as_ref())
+        .map_err(|_| Error::FailtoGenSigKeyPair)?;
+
+    Ok((server_msl, server_sig_kp))
+}
+
+fn send_connect_msg_inner(
+    sender_msl: &mut MinimalSecureLayer,
+    sender_sig_kp: &Ed25519KeyPair,
+    receiver_msl: &mut MinimalSecureLayer,
+    custom_datas: Option<Vec<u8>>,
+) -> Result<Option<Message>> {
+    // Create and sign connect message
+    let connect_msg = sender_msl.create_connect_message(
+        sender_sig_kp.public_key().as_ref(),
+        custom_datas.as_opt_ref(),
+    )?;
+    let sig = sender_sig_kp.sign(&connect_msg);
+
+    // Write connect message and it's sig in channel
+    let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+    channel
+        .write(&connect_msg)
+        .map_err(|_| Error::BufferFlushError)?;
+    channel
+        .write(sig.as_ref())
+        .map_err(|_| Error::BufferFlushError)?;
+
+    // Receiver read connect message from channel
+    let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+    receiver_msl.read(&channel[..])
+}
+
+#[inline]
+fn send_connect_msg(
+    sender_msl: &mut MinimalSecureLayer,
+    sender_sig_kp: &Ed25519KeyPair,
+    receiver_msl: &mut MinimalSecureLayer,
+    custom_datas: Option<Vec<u8>>,
+) -> Result<()> {
+    let connect_msg_received = send_connect_msg_inner(
+        sender_msl,
+        sender_sig_kp,
+        receiver_msl,
+        custom_datas.clone(),
+    )?
+    .expect("Must be receive a message");
+    assert_eq!(
+        Message::Connect {
+            sig_algo: SIG_ALGO_ED25519_ARRAY,
+            sig_pubkey: sender_sig_kp.public_key().as_ref().to_vec(),
+            custom_datas,
+        },
+        connect_msg_received,
+    );
+    Ok(())
+}
+
+#[inline]
+fn send_ack_msg(
+    sender_msl: &mut MinimalSecureLayer,
+    sender_sig_kp: &Ed25519KeyPair,
+    receiver_msl: &mut MinimalSecureLayer,
+    custom_datas: Option<Vec<u8>>,
+) -> Result<()> {
+    let msg_received = send_ack_msg_inner(
+        sender_msl,
+        sender_sig_kp,
+        receiver_msl,
+        custom_datas.as_opt_ref(),
+    )?
+    .expect("Must be receive a message");
+    assert_eq!(Message::Ack { custom_datas }, msg_received,);
+    Ok(())
+}
+
+fn send_ack_msg_inner<'a>(
+    sender_msl: &mut MinimalSecureLayer,
+    sender_sig_kp: &Ed25519KeyPair,
+    receiver_msl: &'a mut MinimalSecureLayer,
+    custom_datas: Option<&[u8]>,
+) -> Result<Option<Message>> {
+    // Create and sign server ack message
+    let ack_msg = sender_msl.create_ack_message(custom_datas)?;
+    let sig = sender_sig_kp.sign(&ack_msg);
+
+    // Write server ack message and it's sig in channel
+    let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+    channel
+        .write(&ack_msg)
+        .map_err(|_| Error::BufferFlushError)?;
+    channel
+        .write(sig.as_ref())
+        .map_err(|_| Error::BufferFlushError)?;
+
+    // Client read server ack message from channel
+    let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+    receiver_msl.read(&channel[..])
+}
+
+#[inline]
+fn send_user_msg(
+    sender_msl: &mut MinimalSecureLayer,
+    receiver_msl: &mut MinimalSecureLayer,
+    datas: Vec<u8>,
+) -> Result<()> {
+    let msg_received = send_user_msg_inner(sender_msl, receiver_msl, datas.clone())?
+        .expect("Must be receive a message");
+    assert_eq!(
+        Message::Message {
+            custom_datas: Some(datas),
+        },
+        msg_received,
+    );
+    Ok(())
+}
+
+fn send_user_msg_inner(
+    sender_msl: &mut MinimalSecureLayer,
+    receiver_msl: &mut MinimalSecureLayer,
+    datas: Vec<u8>,
+) -> Result<Option<Message>> {
+    // Client write one message in channel
+    let mut channel = BufWriter::new(Vec::with_capacity(1_000));
+    sender_msl.write_message(&datas, &mut channel)?;
+
+    // Server read client message from channel
+    let channel = channel.into_inner().map_err(|_| Error::BufferFlushError)?;
+    receiver_msl.read(&channel[..])
+}
+
+#[test]
+fn server_recv_ack_early() -> Result<()> {
+    //////////////////////////
+    // SERVER INFOS
+    //////////////////////////
+
+    let (mut server_msl, server_sig_kp) = server_infos()?;
+
+    //////////////////////////
+    // CLIENT INFOS
+    //////////////////////////
+
+    let (mut client_msl, client_sig_kp) = client_infos(server_sig_kp.public_key().as_ref())?;
+
+    //////////////////////////
+    // SERVER CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut server_msl,
+        &server_sig_kp,
+        &mut client_msl,
+        Some(vec![5, 1, 1, 5]),
+    )?;
+
+    //////////////////////////
+    // CLIENT ACK MSG
+    //////////////////////////
+
+    assert_eq!(
+        None,
+        send_ack_msg_inner(
+            &mut client_msl,
+            &client_sig_kp,
+            &mut server_msl,
+            Some(&[7, 1, 1, 7]),
+        )?
+    );
+
+    //////////////////////////
+    // CLIENT CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut client_msl,
+        &client_sig_kp,
+        &mut server_msl,
+        Some(vec![5, 4, 4, 5]),
+    )?;
+
+    //////////////////////////
+    // SERVER ACK MSG
+    //////////////////////////
+
+    send_ack_msg(
+        &mut server_msl,
+        &server_sig_kp,
+        &mut client_msl,
+        Some(vec![3, 1, 1, 3]),
+    )?;
+
+    /////////////////////////////////////////
+    // RECEIVE CLIENT USER MSG TOO EARLY
+    /////////////////////////////////////////
+
+    assert_eq!(
+        None,
+        send_user_msg_inner(&mut client_msl, &mut server_msl, vec![5, 2, 2, 5],)?
+    );
+
+    //////////////////////////////////////////
+    // GET CLIENT ACK MSG RECEIVED TOO EARLY
+    //////////////////////////////////////////
+
+    assert_eq!(
+        Some(Message::Ack {
+            custom_datas: Some(vec![7, 1, 1, 7]),
+        }),
+        server_msl.take_ack_msg_recv_too_early()?
+    );
+
+    assert_eq!(None, server_msl.take_ack_msg_recv_too_early()?);
+
+    //////////////////////////////////////////
+    // GET CLIENT USER MSG RECEIVED TOO EARLY
+    //////////////////////////////////////////
+
+    assert_eq!(
+        vec![Message::Message {
+            custom_datas: Some(vec![5, 2, 2, 5]),
+        }],
+        server_msl.drain_tmp_stack_user_msgs()?
+    );
+
+    assert_eq!(
+        Vec::<Message>::with_capacity(0),
+        server_msl.drain_tmp_stack_user_msgs()?
+    );
+
+    Ok(())
+}
+
+#[test]
+fn disordered_passing_case() -> Result<()> {
+    //////////////////////////
+    // SERVER INFOS
+    //////////////////////////
+
+    let (mut server_msl, server_sig_kp) = server_infos()?;
+
+    //////////////////////////
+    // CLIENT INFOS
+    //////////////////////////
+
+    let (mut client_msl, client_sig_kp) = client_infos(server_sig_kp.public_key().as_ref())?;
+
+    //////////////////////////
+    // CLIENT CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut client_msl,
+        &client_sig_kp,
+        &mut server_msl,
+        Some(vec![5, 4, 4, 5]),
+    )?;
+
+    //////////////////////////
+    // SERVER ACK MSG
+    //////////////////////////
+
+    send_ack_msg(
+        &mut server_msl,
+        &server_sig_kp,
+        &mut client_msl,
+        Some(vec![5, 8, 8, 5]),
+    )?;
+
+    //////////////////////////
+    // SERVER CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut server_msl,
+        &server_sig_kp,
+        &mut client_msl,
+        Some(vec![5, 1, 1, 5]),
+    )?;
+
+    //////////////////////////
+    // CLIENT ACK MSG
+    //////////////////////////
+
+    send_ack_msg(
+        &mut client_msl,
+        &client_sig_kp,
+        &mut server_msl,
+        Some(vec![7, 4, 4, 7]),
+    )?;
+
+    //////////////////////////
+    // SERVER USER MSG
+    //////////////////////////
+
+    send_user_msg(&mut server_msl, &mut client_msl, vec![1, 5, 5, 1])?;
+
+    Ok(())
+}
+
+#[test]
+fn test_middle_man_detection() -> Result<()> {
+    //////////////////////////
+    // SERVER INFOS
+    //////////////////////////
+
+    let (_server_msl, server_sig_kp) = server_infos()?;
+
+    //////////////////////////
+    // MIDDLE MAN INFOS
+    //////////////////////////
+
+    let (mut middle_msl, middle_sig_kp) = server_infos()?;
+
+    //////////////////////////
+    // CLIENT INFOS
+    //////////////////////////
+
+    let (mut client_msl, client_sig_kp) = client_infos(server_sig_kp.public_key().as_ref())?;
+
+    //////////////////////////
+    // CLIENT CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut client_msl,
+        &client_sig_kp,
+        &mut middle_msl,
+        Some(vec![7, 6, 5, 4]),
+    )?;
+
+    //////////////////////////
+    // MIDDLE MAN CONNECT MSG
+    //////////////////////////
+
+    let result = send_connect_msg(
+        &mut middle_msl,
+        &middle_sig_kp,
+        &mut client_msl,
+        Some(vec![7, 5, 6, 4]),
+    );
+    if let Err(Error::UnexpectedRemoteSigPubKey) = result {
+        Ok(())
+    } else {
+        println!("unexpected result={:?}", result);
+        panic!();
+    }
+}
+
+#[test]
+fn ordered_passing_case() -> Result<()> {
+    //////////////////////////
+    // SERVER INFOS
+    //////////////////////////
+
+    let (mut server_msl, server_sig_kp) = server_infos()?;
+
+    //////////////////////////
+    // CLIENT INFOS
+    //////////////////////////
+
+    let (mut client_msl, client_sig_kp) = client_infos(server_sig_kp.public_key().as_ref())?;
+
+    //////////////////////////
+    // CLIENT CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut client_msl,
+        &client_sig_kp,
+        &mut server_msl,
+        Some(vec![5, 4, 4, 5]),
+    )?;
+
+    //////////////////////////
+    // SERVER CONNECT MSG
+    //////////////////////////
+
+    send_connect_msg(
+        &mut server_msl,
+        &server_sig_kp,
+        &mut client_msl,
+        Some(vec![5, 6, 6, 5]),
+    )?;
+
+    //////////////////////////
+    // SERVER ACK MSG
+    //////////////////////////
+
+    send_ack_msg(
+        &mut server_msl,
+        &server_sig_kp,
+        &mut client_msl,
+        Some(vec![5, 8, 8, 5]),
+    )?;
+
+    //////////////////////////
+    // CLIENT ACK MSG
+    //////////////////////////
+
+    send_ack_msg(
+        &mut client_msl,
+        &client_sig_kp,
+        &mut server_msl,
+        Some(vec![5, 9, 9, 5]),
+    )?;
+
+    //////////////////////////
+    // CLIENT USER MSG
+    //////////////////////////
+
+    send_user_msg(&mut client_msl, &mut server_msl, vec![5, 7, 7, 5])?;
+
+    //////////////////////////
+    // SERVER USER MSG
+    //////////////////////////
+
+    send_user_msg(&mut server_msl, &mut client_msl, vec![7, 4, 4, 7])?;
+
+    Ok(())
+}
-- 
GitLab