diff --git a/LevelDB.cpp b/LevelDB.cpp index 4c9f8901..5cee7bdd 100644 --- a/LevelDB.cpp +++ b/LevelDB.cpp @@ -28,6 +28,7 @@ #include #include "leveldb/db.h" +#include "leveldb/write_batch.h" #include #include "LevelDB.h" @@ -96,6 +97,42 @@ void LevelDB::writeRawString(std::string_view _key, const string &_value) { throwExceptionOnError(status); } +void LevelDB::writeBatch(const vector> &puts, + const vector &deletes, + bool requireNewPutKeys) { + lock_guard lock(mutex); + + // make sure no keys with same names existed before + if (requireNewPutKeys) { + for (const auto &it : puts) { + if (readString(it.first) != nullptr) { + throw SGXException(KEY_NAME_ALREADY_EXISTS, + string(__FUNCTION__) + + ":Name already exists: " + it.first); + } + } + } + + leveldb::WriteBatch batch; + Json::FastWriter fastWriter; + + for (const auto &it : puts) { + Json::Value writerData; + writerData["value"] = it.second; + writerData["timestamp"] = std::to_string(std::time(nullptr)); + std::string output = fastWriter.write(writerData); + + batch.Put(Slice(it.first), Slice(output)); + } + + for (const auto &key : deletes) { + batch.Delete(Slice(key)); + } + + auto status = db->Write(writeOptions, &batch); + throwExceptionOnError(status); +} + void LevelDB::deleteDHDKGKey(std::string_view _key) { string full_key = string(WalletDBKeys::DKG_DH_KEY_PREFIX) + string(_key); diff --git a/LevelDB.h b/LevelDB.h index 6ffe190c..91547da7 100644 --- a/LevelDB.h +++ b/LevelDB.h @@ -90,6 +90,15 @@ class LevelDB { */ void writeRawString(std::string_view key1, const string &value1); + /** + * @brief Atomically writes and deletes keys in a single LevelDB batch. + * Values in puts are wrapped in the standard JSON envelope with timestamp. + * If requireNewPutKeys is true, all put keys must not already exist. + */ + void writeBatch(const vector> &puts, + const vector &deletes, + bool requireNewPutKeys = false); + void writeDataUnique(std::string_view Name, const string &value); void deleteDHDKGKey(std::string_view _key); diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index c7afa71d..a9347ac0 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -43,6 +43,7 @@ #include "SGXException.h" #include "TECrypto.h" #include "WalletDBKeys.h" +#include #include "SGXWalletServer.h" #include "SGXWalletServer.hpp" @@ -536,7 +537,8 @@ Json::Value SGXWalletServer::generateDKGPolyImpl(const string &_polyName, } Json::Value SGXWalletServer::generateDKGPolyV3Impl( - const string &_polyName, const string &_previousBLSPrivateKeyName, int _t) { + const string &_polyName, const string &_previousBLSPrivateKeyName, int _t, + int _n, const Json::Value &_publicKeys) { COUNT_STATISTICS spdlog::info("Entering {}", __FUNCTION__); INIT_RESULT(result) @@ -561,12 +563,68 @@ Json::Value SGXWalletServer::generateDKGPolyV3Impl( string(__FUNCTION__) + ":Invalid gen dkg param t "); } + if (!check_n_t(_t, _n)) { + throw SGXException(GENERATE_DKGV3_POLY_INVALID_PARAMS, + string(__FUNCTION__) + ":Invalid gen dkg params n/t"); + } + + if (!_publicKeys.isArray() || (int)_publicKeys.size() != _n) { + throw SGXException(GENERATE_DKGV3_POLY_INVALID_PUBKEY_COUNT, + string(__FUNCTION__) + + ":publicKeys size must equal n"); + } + + vector pubKeyStrs; + pubKeyStrs.reserve(_n); + for (int i = 0; i < _n; i++) { + if (!checkHex(_publicKeys[i].asString(), + DKG_ECDSA_PUBLIC_KEY_NUM_BYTES)) { + throw SGXException(GENERATE_DKGV3_POLY_INVALID_PUBKEY_HEX, + string(__FUNCTION__) + + ":Invalid public key at index " + to_string(i)); + } + pubKeyStrs.push_back(_publicKeys[i].asString()); + } + std::shared_ptr encryptedBLSKey = readFromDb(_previousBLSPrivateKeyName); CHECK_STATE(encryptedBLSKey); encrPolyHex = genDkgPolyV3(_t, *encryptedBLSKey); - writeDataToDB(_polyName, encrPolyHex); + + // Build and persist binding metadata + string joinedKeys; + for (const auto &k : pubKeyStrs) { + joinedKeys += k; + joinedKeys += ","; + } + if (!joinedKeys.empty()) + joinedKeys.pop_back(); + string recipientsHash = cryptlite::sha256::hash_hex(joinedKeys); + + Json::Value meta; + meta["version"] = 1; + meta["polyName"] = _polyName; + meta["t"] = _t; + meta["n"] = _n; + for (int i = 0; i < _n; i++) { + meta["publicKeys"][i] = pubKeyStrs[i]; + } + meta["recipientsHash"] = recipientsHash; + meta["previousBLSPrivateKeyName"] = _previousBLSPrivateKeyName; + meta["createdAt"] = (Json::Int64)chrono::duration_cast( + chrono::system_clock::now().time_since_epoch()) + .count(); + + Json::FastWriter writer; + string metaKey = string(WalletDBKeys::DKG_META_V1_PREFIX) + _polyName; + + vector> puts; + puts.emplace_back(_polyName, encrPolyHex); + puts.emplace_back(metaKey, writer.write(meta)); + + // Poly and metadata must be persisted atomically to avoid fail-open paths. + LevelDB::getLevelDb()->writeBatch(puts, {}, true); } HANDLE_SGX_EXCEPTION(result) @@ -633,7 +691,7 @@ Json::Value SGXWalletServer::getSecretShareImpl(const string &_polyName, vector pubKeysStrs; for (int i = 0; i < _n; i++) { - if (!checkHex(_pubKeys[i].asString(), 64)) { + if (!checkHex(_pubKeys[i].asString(), DKG_ECDSA_PUBLIC_KEY_NUM_BYTES)) { throw SGXException(INVALID_DKG_GETSS_KEY_HEX, string(__FUNCTION__) + ":Invalid public key"); } @@ -876,16 +934,24 @@ Json::Value SGXWalletServer::complaintResponseImpl(const string &_polyName, } } + vector keysToDelete; + keysToDelete.reserve(static_cast(_n) * 2 + 3); + for (int i = 0; i < _n; i++) { string name = _polyName + "_" + to_string(i) + ":"; - LevelDB::getLevelDb()->deleteDHDKGKey(name); + keysToDelete.push_back(string(WalletDBKeys::DKG_DH_KEY_PREFIX) + name); string shareG2_name = "shareG2_" + _polyName + "_" + to_string(i) + ":"; - LevelDB::getLevelDb()->deleteKey(shareG2_name); + keysToDelete.push_back(shareG2_name); } - LevelDB::getLevelDb()->deleteKey(_polyName); + keysToDelete.push_back(_polyName); string encryptedSecretShareName = "encryptedSecretShare:" + _polyName; - LevelDB::getLevelDb()->deleteKey(encryptedSecretShareName); + keysToDelete.push_back(encryptedSecretShareName); + + string metaKey = string(WalletDBKeys::DKG_META_V1_PREFIX) + _polyName; + keysToDelete.push_back(metaKey); + + LevelDB::getLevelDb()->writeBatch({}, keysToDelete, false); } HANDLE_SGX_EXCEPTION(result) @@ -988,11 +1054,47 @@ Json::Value SGXWalletServer::getSecretShareV2Impl(const string &_polyName, ":Invalid DKG parameters: n or t "); } + // If V3 binding metadata exists for this poly, enforce exact match - + // Enforce same security guarantees as new getSecretShareV3Impl + string metaKey = string(WalletDBKeys::DKG_META_V1_PREFIX) + _polyName; + shared_ptr metaStr = checkDataFromDb(metaKey); + // If exists, then poly was created as V3 - need same security guarantees + if (metaStr != nullptr) { + Json::Value meta; + Json::Reader reader; + if (!reader.parse(*metaStr, meta)) { + throw SGXException(INVALID_DKG_GETSS_V2_POLY_NAME, + string(__FUNCTION__) + + ":Failed to parse DKG metadata"); + } + if (meta["t"].asInt() != _t || meta["n"].asInt() != _n) { + throw SGXException(INVALID_DKG_GETSS_V2_META_PARAMS_MISMATCH, + string(__FUNCTION__) + + ":t/n mismatch with bound metadata"); + } + const Json::Value &boundKeys = meta["publicKeys"]; + if (boundKeys.size() != _pubKeys.size()) { + throw SGXException( + INVALID_DKG_GETSS_V2_META_PUBKEYS_MISMATCH, + string(__FUNCTION__) + + ":publicKeys count mismatch with bound metadata"); + } + for (Json::ArrayIndex i = 0; i < boundKeys.size(); i++) { + if (boundKeys[i].asString() != _pubKeys[i].asString()) { + throw SGXException( + INVALID_DKG_GETSS_V2_META_PUBKEYS_MISMATCH, + string(__FUNCTION__) + + ":publicKey mismatch with bound metadata at index " + + to_string(i)); + } + } + } + shared_ptr encrPoly = readFromDb(_polyName); vector pubKeysStrs; for (int i = 0; i < _n; i++) { - if (!checkHex(_pubKeys[i].asString(), 64)) { + if (!checkHex(_pubKeys[i].asString(), DKG_ECDSA_PUBLIC_KEY_NUM_BYTES)) { throw SGXException(INVALID_DKG_GETSS_V2_PUBKEY_HEX, string(__FUNCTION__) + ":Invalid public key"); } @@ -1016,6 +1118,100 @@ Json::Value SGXWalletServer::getSecretShareV2Impl(const string &_polyName, RETURN_SUCCESS(result) } +Json::Value SGXWalletServer::getSecretShareV3Impl(const string &_polyName) { + COUNT_STATISTICS + spdlog::info("Entering {}", __FUNCTION__); + INIT_RESULT(result); + result["secretShare"] = ""; + + try { + if (!checkName(_polyName, "POLY")) { + throw SGXException(INVALID_DKG_GETSS_V3_POLY_NAME, + string(__FUNCTION__) + ":Invalid polynomial name"); + } + + string metaKey = string(WalletDBKeys::DKG_META_V1_PREFIX) + _polyName; + shared_ptr metaStr = checkDataFromDb(metaKey); + if (metaStr == nullptr) { + throw SGXException( + INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":No V3 binding metadata found for poly: " + _polyName); + } + + Json::Value meta; + Json::Reader reader; + if (!reader.parse(*metaStr, meta)) { + throw SGXException(INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":Failed to parse DKG metadata for: " + _polyName); + } + + if (!meta.isObject() || !meta.isMember("t") || !meta["t"].isInt() || + !meta.isMember("n") || !meta["n"].isInt()) { + throw SGXException( + INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":Invalid DKG metadata structure for: " + _polyName); + } + + int t = meta["t"].asInt(); + int n = meta["n"].asInt(); + + if (!check_n_t(t, n)) { + throw SGXException( + INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":Invalid DKG metadata params n/t for: " + _polyName); + } + + if (!meta.isMember("publicKeys") || !meta["publicKeys"].isArray()) { + throw SGXException( + INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":Missing metadata publicKeys array for: " + _polyName); + } + + const Json::Value &boundKeys = meta["publicKeys"]; + if ((int)boundKeys.size() != n) { + throw SGXException( + INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":Metadata publicKeys size mismatch for: " + _polyName); + } + + vector pubKeyStrs; + pubKeyStrs.reserve(n); + for (int i = 0; i < n; i++) { + if (!boundKeys[i].isString() || + !checkHex(boundKeys[i].asString(), DKG_ECDSA_PUBLIC_KEY_NUM_BYTES)) { + throw SGXException(INVALID_DKG_GETSS_V3_NO_METADATA, + string(__FUNCTION__) + + ":Invalid metadata public key at index " + + to_string(i)); + } + pubKeyStrs.push_back(boundKeys[i].asString()); + } + + shared_ptr encrPoly = readFromDb(_polyName); + + string secret_share_name = "encryptedSecretShare:" + _polyName; + shared_ptr encryptedSecretShare = + checkDataFromDb(secret_share_name); + + if (encryptedSecretShare != nullptr) { + result["secretShare"] = *encryptedSecretShare.get(); + } else { + string s = + getSecretSharesV2(_polyName, encrPoly->c_str(), pubKeyStrs, t, n); + result["secretShare"] = s; + } + } + HANDLE_SGX_EXCEPTION(result) + + RETURN_SUCCESS(result) +} + Json::Value SGXWalletServer::dkgVerificationV2Impl(const string &_publicShares, const string &_ethKeyName, const string &_secretShare, @@ -1261,16 +1457,24 @@ Json::Value SGXWalletServer::createBLSPrivateKeyV3Impl( // Delete SGX data related to the DKG process only if polyName was passed if (hasCleanupPolyName) { + vector keysToDelete; + keysToDelete.reserve(static_cast(_n) * 2 + 3); + for (int i = 0; i < _n; i++) { string name = _polyName + "_" + to_string(i) + ":"; - LevelDB::getLevelDb()->deleteDHDKGKey(name); + keysToDelete.push_back(string(WalletDBKeys::DKG_DH_KEY_PREFIX) + name); string shareG2_name = "shareG2_" + _polyName + "_" + to_string(i) + ":"; - LevelDB::getLevelDb()->deleteKey(shareG2_name); + keysToDelete.push_back(shareG2_name); } - LevelDB::getLevelDb()->deleteKey(_polyName); + keysToDelete.push_back(_polyName); string encryptedSecretShareName = "encryptedSecretShare:" + _polyName; - LevelDB::getLevelDb()->deleteKey(encryptedSecretShareName); + keysToDelete.push_back(encryptedSecretShareName); + + string metaKey = string(WalletDBKeys::DKG_META_V1_PREFIX) + _polyName; + keysToDelete.push_back(metaKey); + + LevelDB::getLevelDb()->writeBatch({}, keysToDelete, false); } } HANDLE_SGX_EXCEPTION(result) @@ -1471,8 +1675,10 @@ Json::Value SGXWalletServer::generateDKGPoly(const string &_polyName, int _t) { } Json::Value SGXWalletServer::generateDKGPolyV3( - const string &_polyName, const string &_previousBLSPrivateKeyName, int _t) { - return generateDKGPolyV3Impl(_polyName, _previousBLSPrivateKeyName, _t); + const string &_polyName, const string &_previousBLSPrivateKeyName, int _t, + int _n, const Json::Value &_publicKeys) { + return generateDKGPolyV3Impl(_polyName, _previousBLSPrivateKeyName, _t, _n, + _publicKeys); } Json::Value SGXWalletServer::getVerificationVector(const string &_polynomeName, @@ -1570,6 +1776,10 @@ Json::Value SGXWalletServer::getSecretShareV2(const string &_polyName, return getSecretShareV2Impl(_polyName, _publicKeys, t, n); } +Json::Value SGXWalletServer::getSecretShareV3(const string &_polyName) { + return getSecretShareV3Impl(_polyName); +} + Json::Value SGXWalletServer::dkgVerificationV2(const string &_publicShares, const string ðKeyName, const string &SecretShare, int t, diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index 890390aa..5dc071b3 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -122,10 +122,13 @@ class SGXWalletServer : public AbstractStubServer { /** * Generates a DKG polynomial using '_previousBLSPrivateKeyName' BLS key * as the polynomial's free coefficient. + * Requires ECDH keys of all recipients to tie them to this polynomial + * for later usage (sharing encrypted secret contribution shares) */ virtual Json::Value generateDKGPolyV3(const string &_polyName, - const string &_previousBLSPrivateKeyName, int _t); + const string &_previousBLSPrivateKeyName, int _t, int _n, + const Json::Value &_publicKeys); virtual Json::Value getVerificationVector(const string &_polynomeName, int _t); @@ -169,6 +172,14 @@ class SGXWalletServer : public AbstractStubServer { const Json::Value &_publicKeys, int t, int n); + /** + * @brief Retrieves secret share contributions for the given polynomial name, + * using the ECDH public keys set on 'generateDKGPolyV3'. + * This call differs from 'getSecretShareV2' by not allowing caller passed + * ECDH keys. + */ + virtual Json::Value getSecretShareV3(const string &_polyName); + virtual Json::Value dkgVerificationV2(const string &_publicShares, const string ðKeyName, const string &SecretShare, int t, int n, @@ -223,7 +234,8 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value generateDKGPolyV3Impl(const string &_polyName, - const string &_previousBLSPrivateKeyName, int _t); + const string &_previousBLSPrivateKeyName, int _t, + int _n, const Json::Value &_publicKeys); static Json::Value getVerificationVectorImpl(const string &_polyName, int _t); @@ -264,6 +276,8 @@ class SGXWalletServer : public AbstractStubServer { const Json::Value &_pubKeys, int _t, int _n); + static Json::Value getSecretShareV3Impl(const string &_polyName); + static Json::Value dkgVerificationV2Impl(const string &_publicShares, const string &_ethKeyName, const string &_secretShare, int _t, diff --git a/TestUtils.cpp b/TestUtils.cpp index b66ac95e..9b90e94a 100644 --- a/TestUtils.cpp +++ b/TestUtils.cpp @@ -385,8 +385,9 @@ RotationDkgData runDKGV3ForRotation(StubClient &c, data.polyNames[contributor] = TestUtils::makeDKGPolyName(schainID, contributor, dkgID); // use previous' DKG BLS private key name - Json::Value response = c.generateDKGPolyV3( - data.polyNames[contributor], v2Data.blsKeyNames[contributor], t); + Json::Value response = c.generateDKGPolyV3(data.polyNames[contributor], + v2Data.blsKeyNames[contributor], + t, n, data.publicEcdsaKeys); CHECK_STATE(response["status"] == 0); // get verification vectors @@ -399,8 +400,7 @@ RotationDkgData runDKGV3ForRotation(StubClient &c, } for (int contributor = 0; contributor < n; ++contributor) { - secretShares[contributor] = c.getSecretShareV2(data.polyNames[contributor], - data.publicEcdsaKeys, t, n); + secretShares[contributor] = c.getSecretShareV3(data.polyNames[contributor]); CHECK_STATE(secretShares[contributor]["status"] == 0); } @@ -518,9 +518,9 @@ runDKGV3ForRotationWithNewNodes(StubClient &c, const RotationDkgData &v2Data, // generate new polynomial for each dealer dealerPolyNames[oldDealerIndex] = TestUtils::makeDKGPolyName( schainID, static_cast(oldDealerIndex), dkgID); - Json::Value response = - c.generateDKGPolyV3(dealerPolyNames[oldDealerIndex], - v2Data.blsKeyNames.at(oldDealerIndex), t); + Json::Value response = c.generateDKGPolyV3( + dealerPolyNames[oldDealerIndex], v2Data.blsKeyNames.at(oldDealerIndex), + t, newN, data.publicEcdsaKeys); CHECK_STATE(response["status"] == 0); // get verification vectors @@ -532,8 +532,8 @@ runDKGV3ForRotationWithNewNodes(StubClient &c, const RotationDkgData &v2Data, // get secret contributions from this dealer - using 'newN' number of points // one for each new node - dealerSecretShares[oldDealerIndex] = c.getSecretShareV2( - dealerPolyNames[oldDealerIndex], data.publicEcdsaKeys, t, newN); + dealerSecretShares[oldDealerIndex] = + c.getSecretShareV3(dealerPolyNames[oldDealerIndex]); CHECK_STATE(dealerSecretShares[oldDealerIndex]["status"] == 0); } diff --git a/WalletDBKeys.h b/WalletDBKeys.h index f229c206..b21a1546 100644 --- a/WalletDBKeys.h +++ b/WalletDBKeys.h @@ -35,6 +35,11 @@ static constexpr const std::string_view BLS_KEY_PREFIX = "BLS_KEY:"; static constexpr const std::string_view POLY_KEY_PREFIX = "POLY:"; static constexpr const std::string_view DKG_DH_KEY_PREFIX = "DKG_DH_KEY_"; +// Plaintext metadata for V3 recipient-bound polynomials. +// Stores t, n, ordered recipient public ECDH keys, and their deterministic +// hash. Not encrypted at rest — contains only public key material. +static constexpr const std::string_view DKG_META_V1_PREFIX = "DKG_META_V1:"; + static constexpr const std::string_view SEK_ENCRYPTED_PAYLOAD_KEY_PREFIXES[] = { ECDSA_KEY_PREFIX, TEMP_ECDSA_KEY_PREFIX, BLS_KEY_PREFIX, POLY_KEY_PREFIX, DKG_DH_KEY_PREFIX}; diff --git a/abstractstubserver.h b/abstractstubserver.h index dc28f1a5..7835d2ec 100644 --- a/abstractstubserver.h +++ b/abstractstubserver.h @@ -79,7 +79,8 @@ class AbstractStubServer : public jsonrpc::AbstractServer { jsonrpc::Procedure( "generateDKGPolyV3", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "polyName", jsonrpc::JSON_STRING, "previousBLSPrivateKeyName", - jsonrpc::JSON_STRING, "t", jsonrpc::JSON_INTEGER, NULL), + jsonrpc::JSON_STRING, "t", jsonrpc::JSON_INTEGER, "n", + jsonrpc::JSON_INTEGER, "publicKeys", jsonrpc::JSON_ARRAY, NULL), &AbstractStubServer::generateDKGPolyV3I); this->bindAndAddMethod(jsonrpc::Procedure("getVerificationVector", jsonrpc::PARAMS_BY_NAME, @@ -156,6 +157,11 @@ class AbstractStubServer : public jsonrpc::AbstractServer { "polyName", jsonrpc::JSON_STRING, "publicKeys", jsonrpc::JSON_ARRAY, "n", jsonrpc::JSON_INTEGER, "t", jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::getSecretShareV2I); + this->bindAndAddMethod(jsonrpc::Procedure("getSecretShareV3", + jsonrpc::PARAMS_BY_NAME, + jsonrpc::JSON_OBJECT, "polyName", + jsonrpc::JSON_STRING, NULL), + &AbstractStubServer::getSecretShareV3I); this->bindAndAddMethod( jsonrpc::Procedure( "dkgVerificationV2", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, @@ -241,7 +247,8 @@ class AbstractStubServer : public jsonrpc::AbstractServer { Json::Value &response) { response = this->generateDKGPolyV3( request["polyName"].asString(), - request["previousBLSPrivateKeyName"].asString(), request["t"].asInt()); + request["previousBLSPrivateKeyName"].asString(), request["t"].asInt(), + request["n"].asInt(), request["publicKeys"]); } inline virtual void getVerificationVectorI(const Json::Value &request, Json::Value &response) { @@ -315,6 +322,10 @@ class AbstractStubServer : public jsonrpc::AbstractServer { request["polyName"].asString(), request["publicKeys"], request["t"].asInt(), request["n"].asInt()); } + inline virtual void getSecretShareV3I(const Json::Value &request, + Json::Value &response) { + response = this->getSecretShareV3(request["polyName"].asString()); + } inline virtual void dkgVerificationV2I(const Json::Value &request, Json::Value &response) { response = this->dkgVerificationV2( @@ -372,7 +383,8 @@ class AbstractStubServer : public jsonrpc::AbstractServer { virtual Json::Value generateDKGPoly(const std::string &polyName, int t) = 0; virtual Json::Value generateDKGPolyV3(const std::string &polyName, - const std::string &previousBLSPrivateKeyName, int t) = 0; + const std::string &previousBLSPrivateKeyName, int t, int n, + const Json::Value &publicKeys) = 0; virtual Json::Value getVerificationVector(const std::string &polyName, int t) = 0; virtual Json::Value getSecretShare(const std::string &polyName, @@ -402,6 +414,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer { virtual Json::Value getSecretShareV2(const std::string &polyName, const Json::Value &publicKeys, int t, int n) = 0; + virtual Json::Value getSecretShareV3(const std::string &polyName) = 0; virtual Json::Value dkgVerificationV2(const std::string &publicShares, const std::string ðKeyName, const std::string &SecretShare, int t, diff --git a/sgxwallet_common.h b/sgxwallet_common.h index 52472178..be83ffe7 100644 --- a/sgxwallet_common.h +++ b/sgxwallet_common.h @@ -75,6 +75,8 @@ extern bool autoconfirm; #define ECDSA_SKEY_BASE 16 #define ECDSA_ENCR_LEN 93 #define ECDSA_BIN_LEN 33 +// Uncompressed ECDSA public key without prefix: X(32 bytes) || Y(32 bytes) +#define DKG_ECDSA_PUBLIC_KEY_NUM_BYTES 64 #define PLAINTEXT_KEY_TOO_LONG -2 #define UNPADDED_KEY -3 @@ -206,6 +208,14 @@ extern bool autoconfirm; #define INVALID_GEN_DKGV3_POLY_NAME -124 #define INVALID_GEN_DKGV3_POLY_PREV_BLS_KEY_NAME -125 #define GENERATE_DKGV3_POLY_INVALID_PARAMS -126 +#define GENERATE_DKGV3_POLY_INVALID_PUBKEY_COUNT -127 +#define GENERATE_DKGV3_POLY_INVALID_PUBKEY_HEX -128 + +#define INVALID_DKG_GETSS_V3_POLY_NAME -129 +#define INVALID_DKG_GETSS_V3_NO_METADATA -130 + +#define INVALID_DKG_GETSS_V2_META_PUBKEYS_MISMATCH -131 +#define INVALID_DKG_GETSS_V2_META_PARAMS_MISMATCH -132 #define SGX_ENCLAVE_ERROR -666 diff --git a/spec.json b/spec.json index 93b4f8c5..80bb5cd2 100644 --- a/spec.json +++ b/spec.json @@ -96,7 +96,14 @@ "params": { "polyName": "POLY:SCHAIN_ID :NODE_ID :DKG_ID", "previousBLSPrivateKeyName": "BLS_KEY:SCHAIN_ID :NODE_ID :DKG_ID", - "t": 3 + "t": 3, + "n": 4, + "publicKeys": [ + "hex_pubkey_0", + "hex_pubkey_1", + "hex_pubkey_2", + "hex_pubkey_3" + ] }, "returns": { "status": 0, @@ -148,6 +155,18 @@ } }, + { + "name": "getSecretShareV3", + "params": { + "polyName": "POLY:SCHAIN_ID :NODE_ID :DKG_ID" + }, + "returns": { + "status": 0, + "errorMessage": "12345", + "secretShare": "123" + } + }, + { "name": "dkgVerification", "params": { diff --git a/stubclient.h b/stubclient.h index ae6df0e5..6fd920f1 100644 --- a/stubclient.h +++ b/stubclient.h @@ -113,11 +113,13 @@ class StubClient : public jsonrpc::Client { Json::Value generateDKGPolyV3(const std::string &polyName, const std::string &previousBLSPrivateKeyName, - int t) { + int t, int n, const Json::Value &publicKeys) { Json::Value p; p["polyName"] = polyName; p["previousBLSPrivateKeyName"] = previousBLSPrivateKeyName; p["t"] = t; + p["n"] = n; + p["publicKeys"] = publicKeys; Json::Value result = this->CallMethod("generateDKGPolyV3", p); if (result.isObject()) return result; @@ -172,6 +174,18 @@ class StubClient : public jsonrpc::Client { result.toStyledString()); } + Json::Value getSecretShareV3(const std::string &polyName) { + Json::Value p; + p["polyName"] = polyName; + Json::Value result = this->CallMethod("getSecretShareV3", p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException( + jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, + result.toStyledString()); + } + Json::Value dkgVerification(const std::string &publicShares, const std::string ðKeyName, const std::string &SecretShare, int t, int n, diff --git a/testw.cpp b/testw.cpp index 7f54ee86..d6c6d706 100644 --- a/testw.cpp +++ b/testw.cpp @@ -1256,9 +1256,14 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, Json::Value previousBlsKey = c.generateBLSPrivateKey(previousBlsKeyName); REQUIRE(previousBlsKey["status"].asInt() == 0); + Json::Value dkgV3PublicKeys(Json::arrayValue); + dkgV3PublicKeys.append(SAMPLE_DKG_PUB_KEY_1); + dkgV3PublicKeys.append(SAMPLE_DKG_PUB_KEY_2); + const string polyName = TestUtils::makeDKGPolyName(schainID, 0, dkgV3ID); Json::Value genPoly = - c.generateDKGPolyV3(polyName, previousBlsKeyName, DKG_V3_API_T); + c.generateDKGPolyV3(polyName, previousBlsKeyName, DKG_V3_API_T, + DKG_V3_API_N, dkgV3PublicKeys); REQUIRE(genPoly["status"].asInt() == 0); Json::Value verificationVector = @@ -1268,16 +1273,29 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, DKG_V3_API_T) .empty()); - Json::Value genPolyWrongName = - c.generateDKGPolyV3("poly", previousBlsKeyName, DKG_V3_API_T); + Json::Value v3SecretShares = c.getSecretShareV3(polyName); + REQUIRE(v3SecretShares["status"].asInt() == 0); + + Json::Value wrongPublicKeys(Json::arrayValue); + wrongPublicKeys.append(SAMPLE_DKG_PUB_KEY_1); + wrongPublicKeys.append( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + Json::Value mismatchedV2Share = + c.getSecretShareV2(polyName, wrongPublicKeys, DKG_V3_API_T, DKG_V3_API_N); + REQUIRE(mismatchedV2Share["status"].asInt() != 0); + + Json::Value genPolyWrongName = c.generateDKGPolyV3( + "poly", previousBlsKeyName, DKG_V3_API_T, DKG_V3_API_N, dkgV3PublicKeys); REQUIRE(genPolyWrongName["status"].asInt() != 0); - Json::Value genPolyWrongPreviousBls = c.generateDKGPolyV3( - TestUtils::makeDKGPolyName(schainID, 1, dkgV3ID), "bls", DKG_V3_API_T); + Json::Value genPolyWrongPreviousBls = + c.generateDKGPolyV3(TestUtils::makeDKGPolyName(schainID, 1, dkgV3ID), + "bls", DKG_V3_API_T, DKG_V3_API_N, dkgV3PublicKeys); REQUIRE(genPolyWrongPreviousBls["status"].asInt() != 0); Json::Value genPolyWrongT = c.generateDKGPolyV3( - TestUtils::makeDKGPolyName(schainID, 2, dkgV3ID), previousBlsKeyName, 33); + TestUtils::makeDKGPolyName(schainID, 2, dkgV3ID), previousBlsKeyName, 33, + DKG_V3_API_N, dkgV3PublicKeys); REQUIRE(genPolyWrongT["status"].asInt() != 0); } @@ -1309,8 +1327,12 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 JSONRPC API creates BLS key", REQUIRE(previousBlsKey["status"].asInt() == 0); polyNames[i] = TestUtils::makeDKGPolyName(schainID, i, dkgV3ID); + } + + for (int i = 0; i < DKG_V3_API_N; ++i) { Json::Value genPoly = - c.generateDKGPolyV3(polyNames[i], previousBlsKeyNames[i], DKG_V3_API_T); + c.generateDKGPolyV3(polyNames[i], previousBlsKeyNames[i], DKG_V3_API_T, + DKG_V3_API_N, publicEcdsaKeys); REQUIRE(genPoly["status"].asInt() == 0); Json::Value verificationVector = @@ -1321,8 +1343,7 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 JSONRPC API creates BLS key", } for (int contributor = 0; contributor < DKG_V3_API_N; ++contributor) { - Json::Value secretShares = c.getSecretShareV2( - polyNames[contributor], publicEcdsaKeys, DKG_V3_API_T, DKG_V3_API_N); + Json::Value secretShares = c.getSecretShareV3(polyNames[contributor]); REQUIRE(secretShares["status"].asInt() == 0); dealerSecretShares[contributor] = secretShares["secretShare"].asString(); REQUIRE(dealerSecretShares[contributor].length() == @@ -1412,9 +1433,13 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 ZMQ API generates DKG polynomial", TestUtils::makeBLSKeyName(schainID, 0, dkgV2ID); REQUIRE(client->generateBLSPrivateKey(previousBlsKeyName)); + Json::Value dkgV3PublicKeys(Json::arrayValue); + dkgV3PublicKeys.append(SAMPLE_DKG_PUB_KEY_1); + dkgV3PublicKeys.append(SAMPLE_DKG_PUB_KEY_2); + const string polyName = TestUtils::makeDKGPolyName(schainID, 0, dkgV3ID); - REQUIRE( - client->generateDKGPolyV3(polyName, previousBlsKeyName, DKG_V3_API_T)); + REQUIRE(client->generateDKGPolyV3(polyName, previousBlsKeyName, DKG_V3_API_T, + DKG_V3_API_N, dkgV3PublicKeys)); Json::Value verificationVector = client->getVerificationVector(polyName, DKG_V3_API_T); @@ -1422,12 +1447,23 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 ZMQ API generates DKG polynomial", DKG_V3_API_T) .empty()); - REQUIRE(!client->generateDKGPolyV3("poly", previousBlsKeyName, DKG_V3_API_T)); + REQUIRE_NOTHROW(client->getSecretShareV3(polyName)); + + Json::Value wrongPublicKeys(Json::arrayValue); + wrongPublicKeys.append(SAMPLE_DKG_PUB_KEY_1); + wrongPublicKeys.append( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + REQUIRE_THROWS(client->getSecretShare(polyName, wrongPublicKeys, DKG_V3_API_T, + DKG_V3_API_N)); + + REQUIRE(!client->generateDKGPolyV3("poly", previousBlsKeyName, DKG_V3_API_T, + DKG_V3_API_N, dkgV3PublicKeys)); REQUIRE_THROWS(client->generateDKGPolyV3( - TestUtils::makeDKGPolyName(schainID, 1, dkgV3ID), "bls", DKG_V3_API_T)); + TestUtils::makeDKGPolyName(schainID, 1, dkgV3ID), "bls", DKG_V3_API_T, + DKG_V3_API_N, dkgV3PublicKeys)); REQUIRE(!client->generateDKGPolyV3( - TestUtils::makeDKGPolyName(schainID, 2, dkgV3ID), previousBlsKeyName, - 33)); + TestUtils::makeDKGPolyName(schainID, 2, dkgV3ID), previousBlsKeyName, 33, + DKG_V3_API_N, dkgV3PublicKeys)); } TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 ZMQ API creates BLS key", @@ -1447,20 +1483,24 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 ZMQ API creates BLS key", vector publicShares(DKG_V3_API_N); vector dealerSecretShares(DKG_V3_API_N); - // generate some BLSkeys and polys to simulate DKG process + // Generate all recipient keys and previous BLS keys first. for (int i = 0; i < DKG_V3_API_N; ++i) { auto ecdsaKey = client->generateECDSAKey(); publicEcdsaKeys.append(ecdsaKey.first); ecdsaKeyNames[i] = ecdsaKey.second; - // generate some BLS key (simulate previous DKG keys) + // Generate previous DKG key material. previousBlsKeyNames[i] = TestUtils::makeBLSKeyName(schainID, i, dkgV2ID); REQUIRE(client->generateBLSPrivateKey(previousBlsKeyNames[i])); - // generate poly using previous DKG key polyNames[i] = TestUtils::makeDKGPolyName(schainID, i, dkgV3ID); + } + + // Generate polys only after the full recipient set is available. + for (int i = 0; i < DKG_V3_API_N; ++i) { REQUIRE(client->generateDKGPolyV3(polyNames[i], previousBlsKeyNames[i], - DKG_V3_API_T)); + DKG_V3_API_T, DKG_V3_API_N, + publicEcdsaKeys)); Json::Value verificationVector = client->getVerificationVector(polyNames[i], DKG_V3_API_T); @@ -1469,8 +1509,8 @@ TEST_CASE_METHOD(TestFixtureDKGV3Api, "DKG V3 ZMQ API creates BLS key", } for (int contributor = 0; contributor < DKG_V3_API_N; ++contributor) { - dealerSecretShares[contributor] = client->getSecretShare( - polyNames[contributor], publicEcdsaKeys, DKG_V3_API_T, DKG_V3_API_N); + dealerSecretShares[contributor] = + client->getSecretShareV3(polyNames[contributor]); REQUIRE(dealerSecretShares[contributor].length() == static_cast(DKG_V3_API_N) * TestUtils::DKG_ENCRYPTED_SECRET_CONTRIBUTION_HEX_LEN); diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index a037b3ab..8cfdc741 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -136,6 +136,8 @@ Json::Value generateDKGPolyReqMessage::process() { Json::Value generateDKGPolyV3ReqMessage::process() { auto polyName = getStringRapid("polyName"); auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); + auto publicKeys = getJsonValueRapid("publicKeys"); auto previousBLSPrivateKeyName = getStringRapid("previousBLSPrivateKeyName"); if (checkKeyOwnership && !isKeyByOwner(previousBLSPrivateKeyName, getStringRapid("cert"))) { @@ -144,7 +146,7 @@ Json::Value generateDKGPolyV3ReqMessage::process() { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::generateDKGPolyV3Impl( - polyName, previousBLSPrivateKeyName, t); + polyName, previousBLSPrivateKeyName, t, n, publicKeys); if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); spdlog::info("Cert {} creates key {}", cert, polyName); @@ -182,6 +184,18 @@ Json::Value getSecretShareReqMessage::process() { return result; } +Json::Value getSecretShareV3ReqMessage::process() { + auto polyName = getStringRapid("polyName"); + if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", + getStringRapid("cert"), polyName); + throw std::invalid_argument("Only owner of the key can access it"); + } + auto result = SGXWalletServer::getSecretShareV3Impl(polyName); + result["type"] = ZMQMessage::GET_SECRET_SHARE_RSP; + return result; +} + Json::Value dkgVerificationReqMessage::process() { auto ethKeyName = getStringRapid("ethKeyName"); auto t = getInt64Rapid("t"); diff --git a/zmq_src/ReqMessage.h b/zmq_src/ReqMessage.h index e0f7e1f4..51c7935d 100644 --- a/zmq_src/ReqMessage.h +++ b/zmq_src/ReqMessage.h @@ -102,6 +102,14 @@ class getSecretShareReqMessage : public ZMQMessage { virtual Json::Value process(); }; +class getSecretShareV3ReqMessage : public ZMQMessage { +public: + getSecretShareV3ReqMessage(shared_ptr &_d) + : ZMQMessage(_d){}; + + virtual Json::Value process(); +}; + class dkgVerificationReqMessage : public ZMQMessage { public: dkgVerificationReqMessage(shared_ptr &_d) diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 899f962b..89a3e27a 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -369,12 +369,14 @@ bool ZMQClient::generateDKGPoly(const string &polyName, int t) { bool ZMQClient::generateDKGPolyV3(const string &polyName, const string &previousBLSPrivateKeyName, - int t) { + int t, int n, const Json::Value &publicKeys) { Json::Value p; p["type"] = ZMQMessage::GENERATE_DKG_POLY_V3_REQ; p["polyName"] = polyName; p["previousBLSPrivateKeyName"] = previousBLSPrivateKeyName; p["t"] = t; + p["n"] = n; + p["publicKeys"] = publicKeys; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); @@ -408,6 +410,17 @@ string ZMQClient::getSecretShare(const string &polyName, return result->getSecretShare(); } +string ZMQClient::getSecretShareV3(const string &polyName) { + Json::Value p; + p["type"] = ZMQMessage::GET_SECRET_SHARE_V3_REQ; + p["polyName"] = polyName; + auto result = + dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getSecretShare(); +} + bool ZMQClient::dkgVerification(const string &publicShares, const string ðKeyName, const string &secretShare, int t, int n, diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 5c90f43c..ab916a15 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -106,13 +106,16 @@ class ZMQClient { bool generateDKGPoly(const string &polyName, int t); bool generateDKGPolyV3(const string &polyName, - const string &previousBLSPrivateKeyName, int t); + const string &previousBLSPrivateKeyName, int t, int n, + const Json::Value &publicKeys); Json::Value getVerificationVector(const string &polyName, int t); string getSecretShare(const string &polyName, const Json::Value &pubKeys, int t, int n); + string getSecretShareV3(const string &polyName); + bool dkgVerification(const string &publicShares, const string ðKeyName, const string &secretShare, int t, int n, int idx); diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 35726108..ddf4a236 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -216,6 +216,9 @@ ZMQMessage::buildRequest(string &_type, shared_ptr _d, case ENUM_GET_SECRET_SHARE_REQ: ret = make_shared(_d); break; + case ENUM_GET_SECRET_SHARE_V3_REQ: + ret = make_shared(_d); + break; case ENUM_DKG_VERIFY_REQ: ret = make_shared(_d); break; @@ -393,6 +396,7 @@ const std::map ZMQMessage::requests{ {GENERATE_DKG_POLY_V3_REQ, ENUM_GENERATE_DKG_POLY_V3_REQ}, {GET_VV_REQ, ENUM_GET_VV_REQ}, {GET_SECRET_SHARE_REQ, ENUM_GET_SECRET_SHARE_REQ}, + {GET_SECRET_SHARE_V3_REQ, ENUM_GET_SECRET_SHARE_V3_REQ}, {DKG_VERIFY_REQ, ENUM_DKG_VERIFY_REQ}, {CREATE_BLS_PRIVATE_REQ, ENUM_CREATE_BLS_PRIVATE_REQ}, {CREATE_BLS_PRIVATE_V3_REQ, ENUM_CREATE_BLS_PRIVATE_V3_REQ}, diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index e2b2d23b..74c87572 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -90,6 +90,7 @@ class ZMQMessage { static constexpr const char *GET_VV_REQ = "getVerificationVectorReq"; static constexpr const char *GET_VV_RSP = "getVerificationVectorRsp"; static constexpr const char *GET_SECRET_SHARE_REQ = "getSecretShareReq"; + static constexpr const char *GET_SECRET_SHARE_V3_REQ = "getSecretShareV3Req"; static constexpr const char *GET_SECRET_SHARE_RSP = "getSecretShareRsp"; static constexpr const char *DKG_VERIFY_REQ = "dkgVerificationReq"; static constexpr const char *DKG_VERIFY_RSP = "dkgVerificationRsp"; @@ -140,6 +141,7 @@ class ZMQMessage { ENUM_GENERATE_DKG_POLY_V3_REQ, ENUM_GET_VV_REQ, ENUM_GET_SECRET_SHARE_REQ, + ENUM_GET_SECRET_SHARE_V3_REQ, ENUM_DKG_VERIFY_REQ, ENUM_CREATE_BLS_PRIVATE_REQ, ENUM_CREATE_BLS_PRIVATE_V3_REQ,