You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by sa...@apache.org on 2020/02/19 16:00:36 UTC
[incubator-milagro-MPC] 01/02: Add NM commit wrappers with test,
benchmark and example
This is an automated email from the ASF dual-hosted git repository.
sandreoli pushed a commit to branch issue8-nm-commit-wrapper
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-MPC.git
commit 33e01281b0b56cac540c585ff9b992558d3b8145
Author: Samuele Andreoli <sa...@yahoo.it>
AuthorDate: Tue Feb 18 16:40:26 2020 +0000
Add NM commit wrappers with test, benchmark and example
---
python/amcl/commitments.py | 222 +++++++++++++++++++++++++++++++++
python/benchmark/bench_nm_commit.py | 47 +++++++
python/examples/example_nm_commit.py | 54 ++++++++
python/test/CMakeLists.txt | 16 ++-
python/test/test_nm_commit.py | 90 +++++++++++++
testVectors/commitments/nm_commit.json | 62 +++++++++
6 files changed, 486 insertions(+), 5 deletions(-)
diff --git a/python/amcl/commitments.py b/python/amcl/commitments.py
new file mode 100644
index 0000000..153addd
--- /dev/null
+++ b/python/amcl/commitments.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+"""
+
+This module use cffi to access the c functions in the amcl_mpc library.
+
+"""
+import platform
+from amcl import core_utils
+
+_ffi = core_utils._ffi
+_ffi.cdef("""
+extern void COMMITMENTS_NM_commit(csprng *RNG, octet *X, octet *R, octet *C);
+extern int COMMITMENTS_NM_decommit(octet* X, octet* R, octet* C);
+""")
+
+if (platform.system() == 'Windows'):
+ libamcl_mpc = _ffi.dlopen("libamcl_mpc.dll")
+ libamcl_core = _ffi.dlopen("libamcl_core.dll")
+elif (platform.system() == 'Darwin'):
+ libamcl_mpc = _ffi.dlopen("libamcl_mpc.dylib")
+ libamcl_core = _ffi.dlopen("libamcl_core.dylib")
+else:
+ libamcl_mpc = _ffi.dlopen("libamcl_mpc.so")
+ libamcl_core = _ffi.dlopen("libamcl_core.so")
+
+# Constants
+SHA256 = 32
+
+OK = 0
+FAIL = 81
+
+
+def to_str(octet_value):
+ """Converts an octet type into a string
+
+ Add all the values in an octet into an array.
+
+ Args::
+
+ octet_value. An octet pointer type
+
+ Returns::
+
+ String
+
+ Raises:
+ Exception
+ """
+ i = 0
+ val = []
+ while i < octet_value.len:
+ val.append(octet_value.val[i])
+ i = i + 1
+ out = b''
+ for x in val:
+ out = out + x
+ return out
+
+
+def make_octet(length, value=None):
+ """Generates an octet pointer
+
+ Generates an empty octet or one filled with the input value
+
+ Args::
+
+ length: Length of empty octet
+ value: Data to assign to octet
+
+ Returns::
+
+ oct_ptr: octet pointer
+ val: data associated with octet to prevent garbage collection
+
+ Raises:
+
+ """
+ oct_ptr = _ffi.new("octet*")
+ if value:
+ val = _ffi.new("char [%s]" % len(value), value)
+ oct_ptr.val = val
+ oct_ptr.max = len(value)
+ oct_ptr.len = len(value)
+ else:
+ val = _ffi.new("char []", length)
+ oct_ptr.val = val
+ oct_ptr.max = length
+ oct_ptr.len = 0
+ return oct_ptr, val
+
+
+def create_csprng(seed):
+ """Make a Cryptographically secure pseudo-random number generator instance
+
+ Make a Cryptographically secure pseudo-random number generator instance
+
+ Args::
+
+ seed: random seed value
+
+ Returns::
+
+ rng: Pointer to cryptographically secure pseudo-random number generator instance
+
+ Raises:
+
+ """
+ seed_val = _ffi.new("char [%s]" % len(seed), seed)
+ seed_len = len(seed)
+
+ # random number generator
+ rng = _ffi.new('csprng*')
+ libamcl_core.RAND_seed(rng, seed_len, seed_val)
+
+ return rng
+
+
+def kill_csprng(rng):
+ """Kill a random number generator
+
+ Deletes all internal state
+
+ Args::
+
+ rng: Pointer to cryptographically secure pseudo-random number generator instance
+
+ Returns::
+
+ Raises:
+
+ """
+ libamcl_core.RAND_clean(rng)
+
+ return 0
+
+
+def nm_commit(rng, x, r=None):
+ """ Commit to the value x
+
+ Generate a commitment c to the value x, using the value r.
+ If r is empty it is randomly generated
+
+ Args::
+
+ rng : Pointer to cryptographically secure pseudo-random generator instance
+ x : value to commit
+ r : random value for the commitment. If empty it is randomly generated
+ If not empty it must be 256 bit long
+
+ Returns::
+
+ Raises::
+
+ """
+
+ if r is None:
+ r_oct, r_val = make_octet(SHA256)
+ else:
+ r_oct, r_val = make_octet(None, r)
+ rng = _ffi.NULL
+
+ _ = r_val # Suppress warning
+
+ x_oct, x_val = make_octet(None, x)
+ c_oct, c_val = make_octet(SHA256)
+ _ = x_val, c_val # Suppress warning
+
+ libamcl_mpc.COMMITMENTS_NM_commit(rng, x_oct, r_oct, c_oct)
+
+ r = to_str(r_oct)
+
+ # Clean memory
+ libamcl_core.OCT_clear(x_oct)
+ libamcl_core.OCT_clear(r_oct)
+
+ return r, to_str(c_oct)
+
+def nm_decommit(x, r, c):
+ """ Decommit commitment c
+
+ Decommit a commitment c to the value x, using the value r.
+
+ Args::
+
+ x : value to commit
+ r : random value for the commitment. It must be 256 bit
+ c : commitment value
+
+ Returns::
+
+ Raises::
+
+ """
+
+ x_oct, x_val = make_octet(None, x)
+ r_oct, r_val = make_octet(None, r)
+ c_oct, c_val = make_octet(None, c)
+ _ = x_val, r_val, c_val # Suppress warning
+
+ ec = libamcl_mpc.COMMITMENTS_NM_decommit(x_oct, r_oct, c_oct)
+
+ return ec
diff --git a/python/benchmark/bench_nm_commit.py b/python/benchmark/bench_nm_commit.py
new file mode 100755
index 0000000..a10012e
--- /dev/null
+++ b/python/benchmark/bench_nm_commit.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+import os
+import sys
+from bench import time_func
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import commitments
+
+x_hex = "40576370e36018f6bfaffc4c66780303a361f0c5f4a18a86a74fb179ca0fcf22"
+r_hex = "296f910bde4530efe3533ed3b74475d6022364db2e57773207734b6daf547ac8"
+
+if __name__ == "__main__":
+ x = bytes.fromhex(x_hex)
+ r = bytes.fromhex(r_hex)
+
+ # Generate quantities for benchmark
+ r, c = commitments.nm_commit(None, x, r)
+
+ assert commitments.nm_decommit(x, r, c) == commitments.OK
+
+ # Run benchmark
+ fncall = lambda: commitments.nm_commit(None, x, r)
+ time_func("nm_commit ", fncall, unit="us")
+
+ fncall = lambda: commitments.nm_decommit(x, r, c)
+ time_func("nm_decommit", fncall, unit="us")
diff --git a/python/examples/example_nm_commit.py b/python/examples/example_nm_commit.py
new file mode 100755
index 0000000..024e60c
--- /dev/null
+++ b/python/examples/example_nm_commit.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import core_utils, commitments
+
+seed_hex = "78d0fb6705ce77dee47d03eb5b9c5d30"
+
+if __name__ == "__main__":
+ seed = bytes.fromhex(seed_hex)
+ rng = core_utils.create_csprng(seed)
+
+ print("Example Non Malleable Commitment")
+ print("Message: BANANA")
+
+ x = b'BANANA'
+
+ # Commitment Phase
+ r, c = commitments.nm_commit(rng, x)
+
+ print("\nCommitment")
+ print(f"\tr = {r.hex()}")
+ print(f"\tc = {c.hex()}")
+
+ # Decommitment Phase. After both c, r and x have been revealed
+ rc = commitments.nm_decommit(x, r, c)
+
+ print("\nDecommitment")
+ if rc == commitments.OK:
+ print("\tSuccess")
+ else:
+ print("\tFailure")
diff --git a/python/test/CMakeLists.txt b/python/test/CMakeLists.txt
index a01ddf4..b2fdbc7 100644
--- a/python/test/CMakeLists.txt
+++ b/python/test/CMakeLists.txt
@@ -44,10 +44,16 @@ file(COPY ${MPC_TV} DESTINATION "${PROJECT_BINARY_DIR}/python/test/mpc/")
file(GLOB SCHNORR_TV "${PROJECT_SOURCE_DIR}/testVectors/schnorr/*.json")
file(COPY ${SCHNORR_TV} DESTINATION "${PROJECT_BINARY_DIR}/python/test/schnorr/")
+# NM Commitment test vector
+file(
+ COPY ${PROJECT_SOURCE_DIR}/testVectors/commitments/nm_commit.json
+ DESTINATION "${PROJECT_BINARY_DIR}/python/test/commitments/")
+
if(NOT CMAKE_BUILD_TYPE STREQUAL "ASan")
- add_python_test(test_python_mpc_mta test_mta.py)
- add_python_test(test_python_mpc_r test_r.py)
- add_python_test(test_python_mpc_s test_s.py)
- add_python_test(test_python_mpc_ecdsa test_ecdsa.py)
- add_python_test(test_python_mpc_schnorr test_schnorr.py)
+ add_python_test(test_python_mpc_mta test_mta.py)
+ add_python_test(test_python_mpc_r test_r.py)
+ add_python_test(test_python_mpc_s test_s.py)
+ add_python_test(test_python_mpc_ecdsa test_ecdsa.py)
+ add_python_test(test_python_mpc_schnorr test_schnorr.py)
+ add_python_test(test_python_mpc_nm_commit test_nm_commit.py)
endif(NOT CMAKE_BUILD_TYPE STREQUAL "ASan")
diff --git a/python/test/test_nm_commit.py b/python/test/test_nm_commit.py
new file mode 100755
index 0000000..a317a79
--- /dev/null
+++ b/python/test/test_nm_commit.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+import os
+import sys
+import json
+from unittest import TestCase
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import commitments, core_utils
+
+# Load and preprocess test vectors
+with open("commitments/nm_commit.json", "r") as f:
+ vectors = json.load(f)
+
+for vector in vectors:
+ for key, val in vector.items():
+ if key != "TEST":
+ vector[key] = bytes.fromhex(val)
+
+
+class TestNMCommit(TestCase):
+ """ Test NM Commitment Commit """
+
+ def setUp(self):
+ # Deterministic PRNG for testing purposes
+ seed_hex = "78d0fb6705ce77dee47d03eb5b9c5d30"
+ seed = bytes.fromhex(seed_hex)
+ self.rng = core_utils.create_csprng(seed)
+
+ self.msg = b'BANANA'
+
+ r_hex = "296f910bde4530efe3533ed3b74475d6022364db2e57773207734b6daf547ac8"
+ c_hex = "b60ebd5193252d22c771a7702724e9922662aae5f634494225cdd3a9e22f9826"
+ self.r_golden = bytes.fromhex(r_hex)
+ self.c_golden = bytes.fromhex(c_hex)
+
+ def test_tv(self):
+ """ Test using test vectors """
+
+ for vector in vectors:
+ r, c = commitments.nm_commit(None, vector['X'], vector['R'])
+
+ self.assertEqual(vector['R'], r)
+ self.assertEqual(vector['C'], c)
+
+ def test_random(self):
+ """ Test using rng """
+ r, c = commitments.nm_commit(self.rng, self.msg)
+
+ self.assertEqual(r, self.r_golden)
+ self.assertEqual(c, self.c_golden)
+
+
+class TestNMDecommit(TestCase):
+ """ Test NM Commitment Decommit """
+
+ def test_tv(self):
+ """ Test using test vectors """
+
+ for vector in vectors:
+ rc = commitments.nm_decommit(vector['X'], vector['R'], vector['C'])
+
+ self.assertEqual(rc, commitments.OK)
+
+ def test_failure(self):
+ """ Test error codes are propagated correctly """
+
+ rc = commitments.nm_decommit(vector['X'], vector['X'], vector['C'])
+
+ self.assertEqual(rc, commitments.FAIL)
diff --git a/testVectors/commitments/nm_commit.json b/testVectors/commitments/nm_commit.json
new file mode 100644
index 0000000..aa506a3
--- /dev/null
+++ b/testVectors/commitments/nm_commit.json
@@ -0,0 +1,62 @@
+[
+ {
+ "TEST":"0",
+ "X": "3f3cc85d2a99534d",
+ "R": "bba1e28251a39a13c037b180ed41cb76c23ca5fd639725706ead86f40d7f7b1e",
+ "C": "d49772a2af3c0904199b5620014f34ee2152cb427a4f241b2e45c62ce1a11f69"
+ },
+ {
+ "TEST": "1",
+ "X": "e8687750094198b455",
+ "R": "3ec7e47abe1345061e4c54808779c43493e4b6d15749d33becd1efb418ec0d34",
+ "C": "4eb9edfe9b66dcdb1882a9ea862794b2308d9a768155e96810502cc361f2afcc"
+ },
+ {
+ "TEST": "2",
+ "X": "ed45810ce81e4bf27980",
+ "R": "18a8edb9f5211df1f0f876e012a1404973cd173acea497d6fb93b5b39b910a89",
+ "C": "52bf67e6e9f4fd68382d2c94dc4044191edf5005cbbfa2cea6a6c7b69846e043"
+ },
+ {
+ "TEST": "3",
+ "X": "965f61c651654622880bcc",
+ "R": "88a038a78dba707e72b192eef24aab965335c51c3e15e39b132229807c828c23",
+ "C": "7e1c0cbcc37b311cabe367bdd85dcacc1fc5e5dcbb374bf12ea224832a6ad91c"
+ },
+ {
+ "TEST": "4",
+ "X": "311b616d5e92ea7e7d6bb8e2",
+ "R": "ec3712c94f66098933c57abbc46c4298bb276eceac3597654e1fd72ba434191d",
+ "C": "a6b6083665669c6de43d0dcd15f29964ffcd9075ecd46ca8e58711ab359002c3"
+ },
+ {
+ "TEST": "5",
+ "X": "1ce58293a6ab70f62b4c724e60",
+ "R": "1c4c20f3d10a5af166e45afbc88c98008fff529b6cc8ce008d375848712a0778",
+ "C": "90986555e5c9bbace9048913ef0eb44a06ed8b5dd76ae3090b4911c6fa2b4d7b"
+ },
+ {
+ "TEST": "6",
+ "X": "e1c508ee1862474e1080bbaefe89",
+ "R": "4d812c8519837c696dc1967adb2452972119bea337174182768fd0f781d41b6d",
+ "C": "83594768dbd29e0fc7dedc0dd999e573a478292305d6764ed3b981d26cc55666"
+ },
+ {
+ "TEST": "7",
+ "X": "df5b2f3e4a2f35e81dc93f9d33b540",
+ "R": "4d9112b5f3b34a3e6db75078e48006b8d86459cfb9ea9093272c416a56a4794b",
+ "C": "e8d031761ed9ab072dec45457710ce7a7d88695b854b8fbd42a06387972cbb79"
+ },
+ {
+ "TEST": "8",
+ "X": "966244192989663e85ee431f90182539",
+ "R": "e330c3a33e2657eca224e778952e1abfde0ca62f8da0417ca64d34b2bb16de3f",
+ "C": "1ef1b10077de18cda783dbbeeefaacc93051d2456e6fc53958506ff1cfda064c"
+ },
+ {
+ "TEST": "9",
+ "X": "76cba6658a3730513c7b7cd2135e3e1f16",
+ "R": "40576370e36018f6bfaffc4c66780303a361f0c5f4a18a86a74fb179ca0fcf22",
+ "C": "88c318a79e481cc7ce7041bb3e66e50cbae6b88efaaa649a4b6b06fb6351b952"
+ }
+]