This is an automated email from the ASF dual-hosted git repository. sandreoli pushed a commit to branch issue9-factoring-zkp-wrapper in repository https://gitbox.apache.org/repos/asf/incubator-milagro-MPC.git
commit 52621af139c442ec7febadf4b93debf623e96a66 Author: Samuele Andreoli <[email protected]> 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" + } +]
