diff --git a/build b/build index 54d697c..9a84a04 100755 --- a/build +++ b/build @@ -19,6 +19,8 @@ echo Building Spark Tests g++ tests/spark_test.cpp src/*.cpp bitcoin/*.cpp bitcoin/support/*.cpp bitcoin/crypto/*.cpp -g -Isecp256k1/include secp256k1/.libs/libsecp256k1.a -lssl -lcrypto -lpthread -lboost_unit_test_framework -std=c++17 -o $1/spark_tests echo Building Address Tests g++ tests/address_test.cpp src/*.cpp bitcoin/*.cpp bitcoin/support/*.cpp bitcoin/crypto/*.cpp -g -Isecp256k1/include secp256k1/.libs/libsecp256k1.a -lssl -lcrypto -lpthread -lboost_unit_test_framework -std=c++17 -o $1/address_tests +echo Building Ownership Tests +g++ tests/ownership_test.cpp src/*.cpp bitcoin/*.cpp bitcoin/support/*.cpp bitcoin/crypto/*.cpp -g -Isecp256k1/include secp256k1/.libs/libsecp256k1.a -lssl -lcrypto -lpthread -lboost_unit_test_framework -std=c++17 -o $1/ownership_tests echo Building Spark Coin Tests g++ tests/coin_test.cpp src/*.cpp bitcoin/*.cpp bitcoin/support/*.cpp bitcoin/crypto/*.cpp -g -Isecp256k1/include secp256k1/.libs/libsecp256k1.a -lssl -lcrypto -lpthread -lboost_unit_test_framework -std=c++17 -o $1/spark_coin_tests echo Building Spark Aead Tests diff --git a/run_all_tests b/run_all_tests index fa07e35..eb3a5d7 100755 --- a/run_all_tests +++ b/run_all_tests @@ -14,6 +14,8 @@ echo Running Spark Tests ./$1/spark_tests echo Running Address Tests ./$1/address_tests +echo Running Ownership Tests +./$1/ownership_tests echo Running Aead Tests ./$1/spark_aead_tests echo Challenge Bpplus Tests diff --git a/src/keys.cpp b/src/keys.cpp index 0957728..7164922 100644 --- a/src/keys.cpp +++ b/src/keys.cpp @@ -1,5 +1,6 @@ #include "keys.h" #include "../bitcoin/hash.h" +#include "transcript.h" namespace spark { @@ -243,4 +244,68 @@ unsigned char Address::decode(const std::string& str) { return network; } +Scalar Address::challenge(const Scalar& m, const GroupElement& A, const GroupElement& H) const { + Transcript transcript(LABEL_TRANSCRIPT_OWNERSHIP); + transcript.add("G", this->params->get_G()); + transcript.add("F", this->params->get_F()); + transcript.add("H", H); + transcript.add("A", A); + transcript.add("m", m); + transcript.add("d", this->d); + transcript.add("Q1", this->Q1); + transcript.add("Q2", this->Q2); + + return transcript.challenge("c"); +} + +void Address::prove_own(const Scalar& m, + const SpendKey& spend_key, + const IncomingViewKey& incomingViewKey, + OwnershipProof& proof) const { + Scalar a, b, c; + a.randomize(); + b.randomize(); + c.randomize(); + + GroupElement H = SparkUtils::hash_div(this->d); + proof.A = H * a + this->params->get_G() * b + this->params->get_F() * c; + + if (proof.A.isInfinity()) { + throw std::invalid_argument("Bad Proof construction!"); + } + + Scalar x = challenge(m, proof.A, H); + + if (x.isZero()) { + throw std::invalid_argument("Unexpected challenge!"); + } + + Scalar x_sqr = x.square(); + + uint64_t i = incomingViewKey.get_diversifier(this->d); + proof.t1 = a + x * spend_key.get_s1(); + proof.t2 = b + x_sqr * spend_key.get_r(); + proof.t3 = c + x_sqr * (SparkUtils::hash_Q2(spend_key.get_s1(), i) + spend_key.get_s2()); +} + +bool Address::verify_own(const Scalar& m, + OwnershipProof& proof) const { + if (proof.A.isInfinity()) { + throw std::invalid_argument("Bad Ownership Proof!"); + } + + GroupElement H = SparkUtils::hash_div(this->d); + Scalar x = challenge(m, proof.A, H); + if (x.isZero()) { + throw std::invalid_argument("Unexpected challenge!"); + } + + Scalar x_sqr = x.square(); + + GroupElement left = proof.A + this->Q1 * x + this->Q2 * x_sqr; + GroupElement right = H * proof.t1 + this->params->get_G() * proof.t2 + this->params->get_F() * proof.t3; + + return left == right; +} + } diff --git a/src/keys.h b/src/keys.h index 4af8b25..574aaf0 100644 --- a/src/keys.h +++ b/src/keys.h @@ -4,6 +4,8 @@ #include "f4grumble.h" #include "params.h" #include "util.h" +#include "../bitcoin/uint256.h" +#include "ownership_proof.h" namespace spark { @@ -82,6 +84,15 @@ class Address { std::string encode(const unsigned char network) const; unsigned char decode(const std::string& str); + Scalar challenge(const Scalar& m, const GroupElement& A, const GroupElement& H) const; + void prove_own(const Scalar& m, + const SpendKey& spend_key, + const IncomingViewKey& incomingViewKey, + OwnershipProof& proof) const; + + bool verify_own(const Scalar& m, + OwnershipProof& proof) const; + private: const Params* params; std::vector d; diff --git a/src/ownership_proof.h b/src/ownership_proof.h new file mode 100644 index 0000000..f8a881f --- /dev/null +++ b/src/ownership_proof.h @@ -0,0 +1,31 @@ +#ifndef FIRO_LIBSPARK_OWNERSHIP_PROOF_H +#define FIRO_LIBSPARK_OWNERSHIP_PROOF_H + +#include "params.h" + +namespace spark { + +class OwnershipProof{ +public: + inline std::size_t memoryRequired() const { + return Scalar::memoryRequired() * 3 + GroupElement::memoryRequired(); + } + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(A); + READWRITE(t1); + READWRITE(t2); + READWRITE(t3); + } + +public: + GroupElement A; + Scalar t1; + Scalar t2; + Scalar t3; +}; +} + +#endif diff --git a/src/util.h b/src/util.h index 66fd6bd..19799e6 100644 --- a/src/util.h +++ b/src/util.h @@ -29,6 +29,7 @@ const std::string LABEL_TRANSCRIPT_BPPLUS = "BULLETPROOF_PLUS_V1"; const std::string LABEL_TRANSCRIPT_CHAUM = "CHAUM_V1"; const std::string LABEL_TRANSCRIPT_GROOTLE = "GROOTLE_V1"; const std::string LABEL_TRANSCRIPT_SCHNORR = "SCHNORR_V1"; +const std::string LABEL_TRANSCRIPT_OWNERSHIP = "OWNERSHIP_V1"; // Generator labels const std::string LABEL_GENERATOR_F = "F"; diff --git a/tests/ownership_test.cpp b/tests/ownership_test.cpp new file mode 100644 index 0000000..245289a --- /dev/null +++ b/tests/ownership_test.cpp @@ -0,0 +1,114 @@ +#include "../src/keys.h" + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN + +#include + +namespace spark { + +class SparkTest {}; +BOOST_FIXTURE_TEST_SUITE(spark_address_ownership_tests, SparkTest) + +BOOST_AUTO_TEST_CASE(serialization) +{ + Scalar m; + m.randomize(); + + OwnershipProof proof; + + const Params* params; + params = Params::get_test(); + + // Generate keys + SpendKey spend_key(params); + FullViewKey full_view_key(spend_key); + IncomingViewKey incoming_view_key(full_view_key); + + // Generate address + const uint64_t i = 12345; + Address address(incoming_view_key, i); + address.prove_own(m, spend_key, incoming_view_key, proof); + + CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION); + serialized << proof; + + OwnershipProof deserialized; + serialized >> deserialized; + + BOOST_CHECK(proof.A == deserialized.A); + BOOST_CHECK(proof.t1 == deserialized.t1); + BOOST_CHECK(proof.t2 == deserialized.t2); + BOOST_CHECK(proof.t3 == deserialized.t3); + +} + +BOOST_AUTO_TEST_CASE(completeness) +{ + Scalar m; + m.randomize(); + + OwnershipProof proof; + + const Params* params; + params = Params::get_test(); + + // Generate keys + SpendKey spend_key(params); + FullViewKey full_view_key(spend_key); + IncomingViewKey incoming_view_key(full_view_key); + + // Generate address + const uint64_t i = 12345; + Address address(incoming_view_key, i); + address.prove_own(m, spend_key, incoming_view_key, proof); + + CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION); + serialized << proof; + + OwnershipProof deserialized; + serialized >> deserialized; + + BOOST_CHECK(address.verify_own(m, deserialized)); +} + +BOOST_AUTO_TEST_CASE(bad_proofs) +{ + Scalar m; + m.randomize(); + + OwnershipProof proof; + + const Params* params; + params = Params::get_test(); + + // Generate keys + SpendKey spend_key(params); + FullViewKey full_view_key(spend_key); + IncomingViewKey incoming_view_key(full_view_key); + + // Generate address + const uint64_t i = 12345; + Address address(incoming_view_key, i); + address.prove_own(m, spend_key, incoming_view_key, proof); + + OwnershipProof evil_proof1 = proof; + evil_proof1.A.randomize(); + BOOST_CHECK(!address.verify_own(m, evil_proof1)); + + OwnershipProof evil_proof2 = proof; + evil_proof2.t1.randomize(); + BOOST_CHECK(!address.verify_own(m, evil_proof2)); + + OwnershipProof evil_proof3 = proof; + evil_proof3.t2.randomize(); + BOOST_CHECK(!address.verify_own(m, evil_proof3)); + + OwnershipProof evil_proof4 = proof; + evil_proof4.t3.randomize(); + BOOST_CHECK(!address.verify_own(m, evil_proof4)); +} + +BOOST_AUTO_TEST_SUITE_END() + +}