Skip to content

Commit

Permalink
security: 🔒 mitigations for timing-attacks on nocoiner's decryption/o…
Browse files Browse the repository at this point in the history
…pening phase

Signed-off-by: Marco Aurélio da Silva <marcoonroad@gmail.com>
  • Loading branch information
marcoonroad committed Sep 7, 2019
1 parent 51fa9e8 commit 62292eb
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 32 deletions.
12 changes: 10 additions & 2 deletions README.md
Expand Up @@ -242,7 +242,14 @@ were executed with major heap compaction disabled to not mask execution time.</i

<p></p>

TODO.
As you can see, there's much more computations performed on valid/bound inputs than on unbound inputs. Inputs
are bound (the opening key and the commitment box) if they were previously computed during commitment phase.
Otherwise, the inputs are unbound _even if they were computed over the same secret during commitment_. This
is a huge important thing when we want a group of commitments (performed by many parties) to be independent
of each other. The security patch introduced on version `1.0.1` uses the [eqaf][10] library to compare in
constant time the MAC tags, and we also force decryption step even if a MAC tag mismatch occurs (obviously
the decrypted plain-text is ignored in this case and the whole opening phase fails).



### Disclaimer
Expand Down Expand Up @@ -271,4 +278,5 @@ process context).
[6]: https://en.wikipedia.org/wiki/Authenticated_encryption
[7]: https://marcoonroad.dev/nocoiner/apiref/nocoiner/Nocoiner/index.html
[8]: https://marcoonroad.dev/nocoiner/apicov/index.html
[9]: https://github.com/marcoonroad/nocoiner/issues/1
[9]: https://github.com/marcoonroad/nocoiner/issues/1
[10]: https://github.com/mirage/eqaf
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
1.0.0
1.0.1
2 changes: 1 addition & 1 deletion dune-project
@@ -1,4 +1,4 @@
(lang dune 1.9)
(name nocoiner)
(version 1.0.0)
(version 1.0.1)
(using fmt 1.1)
3 changes: 2 additions & 1 deletion lib/dune
Expand Up @@ -2,7 +2,8 @@
(name nocoiner)
(public_name nocoiner)
(wrapped true)
(libraries core nocrypto.unix nocrypto digestif digestif.c scrypt-kdf)
(libraries core nocrypto.unix nocrypto digestif digestif.c eqaf eqaf.cstruct
scrypt-kdf)
(synopsis "The Nocoiner module for nocoiner library.")
(preprocess
(pps bisect_ppx -conditional -no-comment-parsing)))
24 changes: 19 additions & 5 deletions lib/encryption.ml
Expand Up @@ -29,14 +29,28 @@ let encrypt ~key ~iv ~metadata ~message:msg =
(ciphertext, tag)


exception DecryptedPlaintext of Cstruct.t

let decrypt ~reason ~key ~iv ~metadata ~cipher ~tag =
let aes_key, mac_key = __kdf key in
let secret = hash mac_key in
let payload = Cstruct.concat [ metadata; iv; cipher ] in
let tag' = mac ~key:secret payload in
if Cstruct.equal tag tag'
then
let aes_key' = AES.of_secret aes_key in
let plaintext = AES.decrypt ~iv ~key:aes_key' cipher in
(* we decypher before the tag verification to avoid
exploitable side-channels vulnerabilities such as
timing attacks. we also check the tags in linear
time regarding the tag size in bytes *)
let aes_key' = AES.of_secret aes_key in
let plaintext = AES.decrypt ~iv ~key:aes_key' cipher in
let decrypted =
Cstruct.of_string @@ Helpers.unpad @@ Cstruct.to_string plaintext
else raise reason
in
(* forces both bound and unbound flows to pass through the exception triggering
pipeline. this is just to approximate both execution timings to reduce the
vector attacks for side-channel attacks *)
try
if Eqaf_cstruct.equal tag tag'
then raise (DecryptedPlaintext decrypted)
else raise reason
with
| DecryptedPlaintext result -> result
5 changes: 4 additions & 1 deletion lib/helpers.ml
Expand Up @@ -14,4 +14,7 @@ let pad ~basis msg =

let __nonzero char = char != __nullchar

let unpad msg = Encoding.decode @@ String.filter ~f:__nonzero msg
(* ignores input if it can't be base64-decoded after dropping null-padding data *)
let unpad msg =
let filtered = String.filter ~f:__nonzero msg in
try Encoding.decode @@ filtered with Failure _ -> msg
3 changes: 2 additions & 1 deletion nocoiner.opam
@@ -1,6 +1,6 @@
opam-version: "2.0"
name: "nocoiner"
version: "1.0.0"
version: "1.0.1"
synopsis: "A Commitment Scheme library for Coin Flipping/Tossing algorithms and sort"
description: """
This project implements Commitment Schemes using the
Expand Down Expand Up @@ -31,5 +31,6 @@ depends: [
"scrypt-kdf" {>= "1.0.0"}
"digestif" {>= "0.7.0"}
"core" {>= "v0.9.1"}
"eqaf" {>= "0.5"}
"bisect_ppx" {>= "1.4.1"}
]
13 changes: 7 additions & 6 deletions test/bench/dune
@@ -1,7 +1,8 @@
(test
(name timing)
(modules timing)
(action (run %{test} time cycles alloc gc percentage speedup samples
-all-values -ascii -fork -no-compactions -overheads -quota 15 -stabilize-gc
-width 300 -v -display tall))
(libraries core_bench nocoiner))
(name timing)
(modules timing)
(action
(run %{test} time cycles alloc gc percentage speedup samples -all-values
-ascii -fork -no-compactions -overheads -quota 15 -stabilize-gc -width
300 -v -display tall))
(libraries core_bench nocoiner))
12 changes: 6 additions & 6 deletions test/bench/timing.expected
@@ -1,11 +1,11 @@
Estimated testing time 45s (3 benchmarks x 15s). Change using -quota SECS.
bound opening: Total time taken 15.1483s (158 samples, max runs 158).
unbound commitment: Total time taken 15.069s (159 samples, max runs 159).
unbound opening: Total time taken 15.0492s (159 samples, max runs 159).
bound opening: Total time taken 15.14s (156 samples, max runs 156).
unbound commitment: Total time taken 15.1478s (158 samples, max runs 158).
unbound opening: Total time taken 15.0146s (157 samples, max runs 157).

Name Runs @ Samples Time/Run Cycls/Run mWd/Run mWd Overhd mjWd/Run mjWd Overhd Prom/Run Prom Overhd mGC/Run mjGC/Run Comp/Run Percentage Speedup
-------------------- ---------------- ---------- ----------- --------- ------------ ---------- ------------- ---------- ------------- ---------- ---------- ---------- ------------ ---------
bound opening 158 @ 158 830.05us 863.66kc 19.77kw 24.18w 17.87w 116.97w 17.87w 116.97w 70.61e-3 0.00e-9 0.00e-9 100.00% 1.03
unbound commitment 159 @ 159 809.76us 842.54kc 19.56kw 28.59w 20.29w -219.19w 20.29w -219.19w 69.79e-3 0.00e-9 0.00e-9 97.56% 1.00
unbound opening 159 @ 159 807.82us 840.52kc 19.56kw 28.59w 20.29w -219.19w 20.29w -219.19w 69.79e-3 0.00e-9 0.00e-9 97.32% 1.00
bound opening 156 @ 156 841.47us 874.71kc 19.78kw 25.82w 18.59w 35.39w 18.59w 35.39w 70.62e-3 0.00e-9 0.00e-9 100.00% 1.02
unbound commitment 158 @ 158 823.70us 856.24kc 19.69kw 23.02w 22.75w -132.15w 22.75w -132.15w 70.32e-3 0.00e-9 0.00e-9 97.89% 1.00
unbound opening 157 @ 157 826.47us 859.12kc 19.69kw 23.02w 22.81w -135.02w 22.81w -135.02w 70.33e-3 0.00e-9 0.00e-9 98.22% 1.00

28 changes: 20 additions & 8 deletions test/bench/timing.ml
Expand Up @@ -2,28 +2,40 @@ open Core_bench.Bench
module Command = Core.Command

let reveals c o =
try ignore @@ Nocoiner.reveal ~commitment:c ~opening:o; true
with Nocoiner.Reasons.BindingFailure -> false
try
ignore @@ Nocoiner.reveal ~commitment:c ~opening:o ;
true
with
| Nocoiner.Reasons.BindingFailure ->
false


let _RIGHT_SECRET = "P = NP would prove God's existence."

let _WRONG_SECRET = "The Quantum Nature is just Godel..."

let (_RIGHT_C, _RIGHT_O) = Nocoiner.commit _RIGHT_SECRET
let (_WRONG_C, _WRONG_O) = Nocoiner.commit _WRONG_SECRET
let _RIGHT_C, _RIGHT_O = Nocoiner.commit _RIGHT_SECRET

let _WRONG_C, _WRONG_O = Nocoiner.commit _WRONG_SECRET

let __test_case_01 () = assert (reveals _RIGHT_C _RIGHT_O)

let __test_case_02 () = assert (not (reveals _WRONG_C _RIGHT_O))

let __test_case_01 ( ) = assert (reveals _RIGHT_C _RIGHT_O)
let __test_case_02 ( ) = assert (not (reveals _WRONG_C _RIGHT_O))
let __test_case_03 ( ) = assert (not (reveals _RIGHT_C _WRONG_O))
let __test_case_03 () = assert (not (reveals _RIGHT_C _WRONG_O))

let _TEST_NAME_01 = "bound opening"

let _TEST_NAME_02 = "unbound commitment"

let _TEST_NAME_03 = "unbound opening"

let __test_01 = Test.create ~name:_TEST_NAME_01 __test_case_01

let __test_02 = Test.create ~name:_TEST_NAME_02 __test_case_02

let __test_03 = Test.create ~name:_TEST_NAME_03 __test_case_03

let suite = [ __test_01; __test_02; __test_03 ]

let _ = Command.run @@ make_command suite

0 comments on commit 62292eb

Please sign in to comment.