Use digests for proposal ids, rather than incrementing integers (#2104)

This commit is contained in:
Amaury Chamayou 2021-01-25 14:05:33 +00:00 коммит произвёл GitHub
Родитель 321cd89fc7
Коммит 32cc0ad938
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 199 добавлений и 93 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -28,3 +28,4 @@ env/
python/setup.py python/setup.py
node_modules node_modules
package-lock.json package-lock.json
.cache

Просмотреть файл

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- Governance proposal ids are now digests, hex-encoded as strings.
## [0.17.1] ## [0.17.1]
### Changed ### Changed

Просмотреть файл

@ -23,21 +23,21 @@ A member proposes to recover the network and other members can vote on the propo
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @accept_recovery.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @accept_recovery.json -H "content-type: application/json"
{ {
"proposal_id": 1, "proposal_id": "1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377",
"proposer_id": 0, "proposer_id": 0,
"state": "OPEN" "state": "OPEN"
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/1/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept.json -H "content-type: application/json"
{ {
"proposal_id": 1, "proposal_id": "1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377",
"proposer_id": 0, "proposer_id": 0,
"state": "OPEN" "state": "OPEN"
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/1/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json"
{ {
"proposal_id": 1, "proposal_id": "1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377",
"proposer_id": 0, "proposer_id": 0,
"state": "ACCEPTED" "state": "ACCEPTED"
} }

Просмотреть файл

@ -61,21 +61,21 @@ To limit the scope of key compromise, members of the consortium can refresh the
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @rekey_ledger.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @rekey_ledger.json -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e",
"proposer_id": 1, "proposer_id": 1,
"state": "OPEN" "state": "OPEN"
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e",
"proposer_id": 1, "proposer_id": 1,
"state": "OPEN" "state": "OPEN"
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e",
"proposer_id": 1, "proposer_id": 1,
"state": "ACCEPTED" "state": "ACCEPTED"
} }
@ -103,21 +103,21 @@ The number of member shares required to restore the private ledger (``recovery_t
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @set_recovery_threshold.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @set_recovery_threshold.json -H "content-type: application/json"
{ {
"proposal_id": 5, "proposal_id": "b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8",
"proposer_id": 1, "proposer_id": 1,
"state": "OPEN" "state": "OPEN"
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
{ {
"proposal_id": 5, "proposal_id": "b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8",
"proposer_id": 1, "proposer_id": 1,
"state": "OPEN" "state": "OPEN"
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
{ {
"proposal_id": 5, "proposal_id": "b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8",
"proposer_id": 1, "proposer_id": 1,
"state": "ACCEPTED" "state": "ACCEPTED"
} }

Просмотреть файл

@ -22,7 +22,7 @@ Then, the certificates of trusted users should be registered in CCF via the memb
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @add_user.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @add_user.json -H "content-type: application/json"
{ {
"proposal_id": 5, "proposal_id": "f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253",
"proposer_id": 0, "proposer_id": 0,
"state": "OPEN" "state": "OPEN"
} }
@ -38,9 +38,9 @@ Other members are then allowed to vote for the proposal, using the proposal id r
} }
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member1_privk --cert member1_cert --data-binary @vote_accept.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253/votes --cacert network_cert --key member1_privk --cert member1_cert --data-binary @vote_accept.json -H "content-type: application/json"
{ {
"proposal_id": 5, "proposal_id": "f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253",
"proposer_id": 0, "proposer_id": 0,
"state": "OPEN" "state": "OPEN"
} }
@ -52,9 +52,9 @@ Other members are then allowed to vote for the proposal, using the proposal id r
} }
} }
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_conditional.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_conditional.json -H "content-type: application/json"
{ {
"proposal_id": 5, "proposal_id": "f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253",
"proposer_id": 0, "proposer_id": 0,
"state": "ACCEPTED" "state": "ACCEPTED"
} }
@ -134,7 +134,7 @@ Once users are added to the opening network, members should create a proposal to
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @open_network.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @open_network.json -H "content-type: application/json"
{ {
"proposal_id": 10, "proposal_id": "77374e16de0b2d61f58aec84d01e6218205d19c9401d2df127d893ce62576b81",
"proposer_id": 0, "proposer_id": 0,
"state": "OPEN" "state": "OPEN"
} }

Просмотреть файл

@ -112,7 +112,7 @@ For example, ``member1`` may submit a proposal to add a new member (``member4``)
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @add_member.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @add_member.json -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
"proposer_id": 1, "proposer_id": 1,
"state": "OPEN" "state": "OPEN"
} }
@ -138,17 +138,17 @@ In this case, a new proposal with id ``4`` has successfully been created and the
} }
# Member 2 rejects the proposal (votes in favour: 1/3) # Member 2 rejects the proposal (votes in favour: 1/3)
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_reject.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_reject.json -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
"proposer_id": 1, "proposer_id": 1,
"state": "OPEN" "state": "OPEN"
} }
# Member 3 accepts the proposal (votes in favour: 2/3) # Member 3 accepts the proposal (votes in favour: 2/3)
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
"proposer_id": 1, "proposer_id": 1,
"state": "ACCEPTED" "state": "ACCEPTED"
} }
@ -167,7 +167,7 @@ The details of pending proposals, including the proposer member id, proposal scr
.. code-block:: bash .. code-block:: bash
# The full proposal state, including votes, can still be retrieved by any member # The full proposal state, including votes, can still be retrieved by any member
$ scurl.sh https://<ccf-node-address>/gov/proposals/4 --cacert networkcert.pem --key member3_privk.pem --cert member3_cert.pem -H "content-type: application/json" -X GET $ scurl.sh https://<ccf-node-address>/gov/proposals/d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd --cacert networkcert.pem --key member3_privk.pem --cert member3_cert.pem -H "content-type: application/json" -X GET
{ {
"parameter": {...}, "parameter": {...},
"proposer": 1, "proposer": 1,
@ -204,7 +204,7 @@ At any stage during the voting process, before the proposal is accepted, the pro
$ scurl.sh https://<ccf-node-address>/gov/proposals/<proposal-id>/withdraw --cacert networkcert.pem --key member1_privk.pem --cert member1_cert.pem -H "content-type: application/json" $ scurl.sh https://<ccf-node-address>/gov/proposals/<proposal-id>/withdraw --cacert networkcert.pem --key member1_privk.pem --cert member1_cert.pem -H "content-type: application/json"
{ {
"proposal_id": 4, "proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
"proposer_id": 1, "proposer_id": 1,
"state": "WITHDRAWN" "state": "WITHDRAWN"
} }

Просмотреть файл

@ -173,7 +173,7 @@
"ProposalInfo": { "ProposalInfo": {
"properties": { "properties": {
"proposal_id": { "proposal_id": {
"$ref": "#/components/schemas/uint64" "$ref": "#/components/schemas/string"
}, },
"proposer_id": { "proposer_id": {
"$ref": "#/components/schemas/uint64" "$ref": "#/components/schemas/uint64"

Просмотреть файл

@ -153,6 +153,7 @@ namespace ccf
tls::Pem member_cert; tls::Pem member_cert;
nlohmann::json member_data; nlohmann::json member_data;
SignedReq signed_request; SignedReq signed_request;
std::vector<uint8_t> request_digest;
}; };
class MemberSignatureAuthnPolicy : public AuthnPolicy class MemberSignatureAuthnPolicy : public AuthnPolicy
@ -187,15 +188,20 @@ namespace ccf
"Members and member certs tables do not match"); "Members and member certs tables do not match");
} }
std::vector<uint8_t> digest;
auto verifier = verifiers.get_verifier(member->cert); auto verifier = verifiers.get_verifier(member->cert);
if (verifier->verify( if (verifier->verify(
signed_request->req, signed_request->sig, signed_request->md)) signed_request->req,
signed_request->sig,
signed_request->md,
digest))
{ {
auto identity = std::make_unique<MemberSignatureAuthnIdentity>(); auto identity = std::make_unique<MemberSignatureAuthnIdentity>();
identity->member_id = member_id.value(); identity->member_id = member_id.value();
identity->member_cert = member->cert; identity->member_cert = member->cert;
identity->member_data = member->member_data; identity->member_data = member->member_data;
identity->signed_request = signed_request.value(); identity->signed_request = signed_request.value();
identity->request_digest = std::move(digest);
return identity; return identity;
} }
else else

Просмотреть файл

@ -93,11 +93,12 @@ namespace ccf
DECLARE_JSON_REQUIRED_FIELDS( DECLARE_JSON_REQUIRED_FIELDS(
Proposal, script, parameter, proposer, state, votes) Proposal, script, parameter, proposer, state, votes)
using Proposals = kv::Map<ObjectId, Proposal>; using ProposalId = std::string;
using Proposals = kv::Map<ProposalId, Proposal>;
struct ProposalInfo struct ProposalInfo
{ {
ObjectId proposal_id; ProposalId proposal_id;
MemberId proposer_id; MemberId proposer_id;
ProposalState state; ProposalState state;
}; };

Просмотреть файл

@ -505,6 +505,24 @@ namespace ccf
return true; return true;
} }
template <>
bool get_path_param(
const enclave::PathParams& params,
const std::string& param_name,
std::string& value,
std::string& error)
{
const auto it = params.find(param_name);
if (it == params.end())
{
error = fmt::format("No parameter named '{}' in path", param_name);
return false;
}
value = it->second;
return true;
}
protected: protected:
EndpointPtr default_endpoint; EndpointPtr default_endpoint;
std::map<std::string, std::map<RESTVerb, EndpointPtr>> std::map<std::string, std::map<RESTVerb, EndpointPtr>>

Просмотреть файл

@ -32,6 +32,8 @@
namespace ccf namespace ccf
{ {
constexpr auto INVALID_PROPOSAL_ID = "INVALID";
static oe_result_t oe_verify_attestation_certificate_with_evidence_cb( static oe_result_t oe_verify_attestation_certificate_with_evidence_cb(
oe_claim_t* claims, size_t claims_length, void* arg) oe_claim_t* claims, size_t claims_length, void* arg)
{ {
@ -356,7 +358,7 @@ namespace ccf
bool set_jwt_public_signing_keys( bool set_jwt_public_signing_keys(
kv::Tx& tx, kv::Tx& tx,
ObjectId proposal_id, const ProposalId& proposal_id,
std::string issuer, std::string issuer,
const JwtIssuerMetadata& issuer_metadata, const JwtIssuerMetadata& issuer_metadata,
const JsonWebKeySet& jwks) const JsonWebKeySet& jwks)
@ -365,9 +367,9 @@ namespace ccf
auto key_issuer = auto key_issuer =
tx.get_view(this->network.jwt_public_signing_key_issuer); tx.get_view(this->network.jwt_public_signing_key_issuer);
auto log_prefix = proposal_id != INVALID_ID ? auto log_prefix = proposal_id == INVALID_PROPOSAL_ID ?
fmt::format("Proposal {}", proposal_id) : "JWT key auto-refresh" :
"JWT key auto-refresh"; fmt::format("Proposal {}", proposal_id);
// add keys // add keys
if (jwks.keys.empty()) if (jwks.keys.empty())
@ -515,7 +517,7 @@ namespace ccf
kv::Tx& tx, kv::Tx& tx,
const CodeDigest& new_code_id, const CodeDigest& new_code_id,
CodeIDs& code_id_table, CodeIDs& code_id_table,
ObjectId proposal_id) const ProposalId& proposal_id)
{ {
auto code_ids = tx.get_view(code_id_table); auto code_ids = tx.get_view(code_id_table);
auto existing_code_id = code_ids->get(new_code_id); auto existing_code_id = code_ids->get(new_code_id);
@ -535,7 +537,7 @@ namespace ccf
kv::Tx& tx, kv::Tx& tx,
const CodeDigest& code_id, const CodeDigest& code_id,
CodeIDs& code_id_table, CodeIDs& code_id_table,
ObjectId proposal_id) const ProposalId& proposal_id)
{ {
auto code_ids = tx.get_view(code_id_table); auto code_ids = tx.get_view(code_id_table);
auto existing_code_id = code_ids->get(code_id); auto existing_code_id = code_ids->get(code_id);
@ -554,41 +556,41 @@ namespace ccf
//! Table of functions that proposal scripts can propose to invoke //! Table of functions that proposal scripts can propose to invoke
const std::unordered_map< const std::unordered_map<
std::string, std::string,
std::function<bool(ObjectId, kv::Tx&, const nlohmann::json&)>> std::function<bool(const ProposalId&, kv::Tx&, const nlohmann::json&)>>
hardcoded_funcs = { hardcoded_funcs = {
// set the js application script // set the js application script
{"set_js_app", {"set_js_app",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const std::string app = args; const std::string app = args;
set_js_scripts(tx, lua::Interpreter().invoke<nlohmann::json>(app)); set_js_scripts(tx, lua::Interpreter().invoke<nlohmann::json>(app));
return true; return true;
}}, }},
// deploy the js application bundle // deploy the js application bundle
{"deploy_js_app", {"deploy_js_app",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto parsed = args.get<DeployJsApp>(); const auto parsed = args.get<DeployJsApp>();
return deploy_js_app(tx, parsed.bundle); return deploy_js_app(tx, parsed.bundle);
}}, }},
// undeploy/remove the js application // undeploy/remove the js application
{"remove_js_app", {"remove_js_app",
[this](ObjectId, kv::Tx& tx, const nlohmann::json&) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json&) {
return remove_js_app(tx); return remove_js_app(tx);
}}, }},
// add/update a module // add/update a module
{"set_module", {"set_module",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto parsed = args.get<SetModule>(); const auto parsed = args.get<SetModule>();
return set_module(tx, parsed.name, parsed.module); return set_module(tx, parsed.name, parsed.module);
}}, }},
// remove a module // remove a module
{"remove_module", {"remove_module",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto name = args.get<std::string>(); const auto name = args.get<std::string>();
return remove_module(tx, name); return remove_module(tx, name);
}}, }},
// add a new member // add a new member
{"new_member", {"new_member",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto parsed = args.get<MemberPubInfo>(); const auto parsed = args.get<MemberPubInfo>();
GenesisGenerator g(this->network, tx); GenesisGenerator g(this->network, tx);
g.add_member(parsed); g.add_member(parsed);
@ -597,7 +599,7 @@ namespace ccf
}}, }},
// retire an existing member // retire an existing member
{"retire_member", {"retire_member",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto member_id = args.get<MemberId>(); const auto member_id = args.get<MemberId>();
GenesisGenerator g(this->network, tx); GenesisGenerator g(this->network, tx);
@ -629,7 +631,10 @@ namespace ccf
return true; return true;
}}, }},
{"set_member_data", {"set_member_data",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto parsed = args.get<SetMemberData>(); const auto parsed = args.get<SetMemberData>();
auto members_view = tx.get_view(this->network.members); auto members_view = tx.get_view(this->network.members);
auto member_info = members_view->get(parsed.member_id); auto member_info = members_view->get(parsed.member_id);
@ -647,7 +652,7 @@ namespace ccf
return true; return true;
}}, }},
{"new_user", {"new_user",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto user_info = args.get<ccf::UserInfo>(); const auto user_info = args.get<ccf::UserInfo>();
GenesisGenerator g(this->network, tx); GenesisGenerator g(this->network, tx);
@ -656,7 +661,10 @@ namespace ccf
return true; return true;
}}, }},
{"remove_user", {"remove_user",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const UserId user_id = args; const UserId user_id = args;
GenesisGenerator g(this->network, tx); GenesisGenerator g(this->network, tx);
@ -670,7 +678,10 @@ namespace ccf
return r; return r;
}}, }},
{"set_user_data", {"set_user_data",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto parsed = args.get<SetUserData>(); const auto parsed = args.get<SetUserData>();
auto users_view = tx.get_view(this->network.users); auto users_view = tx.get_view(this->network.users);
auto user_info = users_view->get(parsed.user_id); auto user_info = users_view->get(parsed.user_id);
@ -688,7 +699,10 @@ namespace ccf
return true; return true;
}}, }},
{"set_ca_cert", {"set_ca_cert",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto parsed = args.get<SetCaCert>(); const auto parsed = args.get<SetCaCert>();
auto ca_certs = tx.get_view(this->network.ca_certs); auto ca_certs = tx.get_view(this->network.ca_certs);
std::vector<uint8_t> cert_der; std::vector<uint8_t> cert_der;
@ -709,14 +723,17 @@ namespace ccf
return true; return true;
}}, }},
{"remove_ca_cert", {"remove_ca_cert",
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) { [this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
const auto cert_name = args.get<std::string>(); const auto cert_name = args.get<std::string>();
auto ca_certs = tx.get_view(this->network.ca_certs); auto ca_certs = tx.get_view(this->network.ca_certs);
ca_certs->remove(cert_name); ca_certs->remove(cert_name);
return true; return true;
}}, }},
{"set_jwt_issuer", {"set_jwt_issuer",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto parsed = args.get<SetJwtIssuer>(); const auto parsed = args.get<SetJwtIssuer>();
auto issuers = tx.get_view(this->network.jwt_issuers); auto issuers = tx.get_view(this->network.jwt_issuers);
auto ca_certs = tx.get_read_only_view(this->network.ca_certs); auto ca_certs = tx.get_read_only_view(this->network.ca_certs);
@ -783,7 +800,10 @@ namespace ccf
return success; return success;
}}, }},
{"remove_jwt_issuer", {"remove_jwt_issuer",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto parsed = args.get<RemoveJwtIssuer>(); const auto parsed = args.get<RemoveJwtIssuer>();
const auto issuer = parsed.issuer; const auto issuer = parsed.issuer;
auto issuers = tx.get_view(this->network.jwt_issuers); auto issuers = tx.get_view(this->network.jwt_issuers);
@ -800,7 +820,10 @@ namespace ccf
return true; return true;
}}, }},
{"set_jwt_public_signing_keys", {"set_jwt_public_signing_keys",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto parsed = args.get<SetJwtPublicSigningKeys>(); const auto parsed = args.get<SetJwtPublicSigningKeys>();
auto issuers = tx.get_view(this->network.jwt_issuers); auto issuers = tx.get_view(this->network.jwt_issuers);
@ -820,7 +843,10 @@ namespace ccf
}}, }},
// accept a node // accept a node
{"trust_node", {"trust_node",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto node_id = args.get<NodeId>(); const auto node_id = args.get<NodeId>();
try try
{ {
@ -837,7 +863,10 @@ namespace ccf
}}, }},
// retire a node // retire a node
{"retire_node", {"retire_node",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto id = args.get<NodeId>(); const auto id = args.get<NodeId>();
auto nodes = tx.get_view(this->network.nodes); auto nodes = tx.get_view(this->network.nodes);
auto node_info = nodes->get(id); auto node_info = nodes->get(id);
@ -860,7 +889,10 @@ namespace ccf
}}, }},
// accept new node code ID // accept new node code ID
{"new_node_code", {"new_node_code",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
return this->add_new_code_id( return this->add_new_code_id(
tx, tx,
args.get<CodeDigest>(), args.get<CodeDigest>(),
@ -869,7 +901,10 @@ namespace ccf
}}, }},
// retire node code ID // retire node code ID
{"retire_node_code", {"retire_node_code",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
return this->retire_code_id( return this->retire_code_id(
tx, tx,
args.get<CodeDigest>(), args.get<CodeDigest>(),
@ -877,7 +912,8 @@ namespace ccf
proposal_id); proposal_id);
}}, }},
{"accept_recovery", {"accept_recovery",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) { [this](
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
if (node.is_part_of_public_network()) if (node.is_part_of_public_network())
{ {
const auto accept_recovery = node.accept_recovery(tx); const auto accept_recovery = node.accept_recovery(tx);
@ -895,7 +931,8 @@ namespace ccf
} }
}}, }},
{"open_network", {"open_network",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) { [this](
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
// On network open, the service checks that a sufficient number of // On network open, the service checks that a sufficient number of
// recovery members have become active. If so, recovery shares are // recovery members have become active. If so, recovery shares are
// allocated to each recovery member // allocated to each recovery member
@ -926,7 +963,8 @@ namespace ccf
return network_opened; return network_opened;
}}, }},
{"rekey_ledger", {"rekey_ledger",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) { [this](
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
const auto ledger_rekeyed = node.rekey_ledger(tx); const auto ledger_rekeyed = node.rekey_ledger(tx);
if (!ledger_rekeyed) if (!ledger_rekeyed)
{ {
@ -935,7 +973,8 @@ namespace ccf
return ledger_rekeyed; return ledger_rekeyed;
}}, }},
{"update_recovery_shares", {"update_recovery_shares",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) { [this](
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
try try
{ {
share_manager.issue_shares(tx); share_manager.issue_shares(tx);
@ -951,7 +990,10 @@ namespace ccf
return true; return true;
}}, }},
{"set_recovery_threshold", {"set_recovery_threshold",
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) { [this](
const ProposalId& proposal_id,
kv::Tx& tx,
const nlohmann::json& args) {
const auto new_recovery_threshold = args.get<size_t>(); const auto new_recovery_threshold = args.get<size_t>();
GenesisGenerator g(this->network, tx); GenesisGenerator g(this->network, tx);
@ -986,7 +1028,7 @@ namespace ccf
}; };
ProposalInfo complete_proposal( ProposalInfo complete_proposal(
kv::Tx& tx, const ObjectId proposal_id, Proposal& proposal) kv::Tx& tx, const ProposalId& proposal_id, Proposal& proposal)
{ {
if (proposal.state != ProposalState::OPEN) if (proposal.state != ProposalState::OPEN)
{ {
@ -1144,14 +1186,14 @@ namespace ccf
} }
static ProposalInfo get_proposal_info( static ProposalInfo get_proposal_info(
ObjectId proposal_id, const Proposal& proposal) const ProposalId& proposal_id, const Proposal& proposal)
{ {
return ProposalInfo{proposal_id, proposal.proposer, proposal.state}; return ProposalInfo{proposal_id, proposal.proposer, proposal.state};
} }
bool get_proposal_id_from_path( bool get_proposal_id_from_path(
const enclave::PathParams& params, const enclave::PathParams& params,
ObjectId& proposal_id, ProposalId& proposal_id,
std::string& error) std::string& error)
{ {
return get_path_param(params, "proposal_id", proposal_id, error); return get_path_param(params, "proposal_id", proposal_id, error);
@ -1292,8 +1334,9 @@ namespace ccf
} }
const auto in = params.get<Propose::In>(); const auto in = params.get<Propose::In>();
const auto proposal_id = get_next_id( const auto proposal_id =
ctx.tx.get_view(this->network.values), ValueIds::NEXT_PROPOSAL_ID); fmt::format("{:02x}", fmt::join(caller_identity.request_digest, ""));
Proposal proposal(in.script, in.parameter, caller_identity.member_id); Proposal proposal(in.script, in.parameter, caller_identity.member_id);
auto proposals = ctx.tx.get_view(this->network.proposals); auto proposals = ctx.tx.get_view(this->network.proposals);
@ -1321,7 +1364,7 @@ namespace ccf
"Member is not active."); "Member is not active.");
} }
ObjectId proposal_id; ProposalId proposal_id;
std::string error; std::string error;
if (!get_proposal_id_from_path( if (!get_proposal_id_from_path(
ctx.rpc_ctx->get_request_path_params(), proposal_id, error)) ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
@ -1362,7 +1405,7 @@ namespace ccf
"Member is not active."); "Member is not active.");
} }
ObjectId proposal_id; ProposalId proposal_id;
std::string error; std::string error;
if (!get_proposal_id_from_path( if (!get_proposal_id_from_path(
ctx.rpc_ctx->get_request_path_params(), proposal_id, error)) ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
@ -1435,7 +1478,7 @@ namespace ccf
"Member is not active."); "Member is not active.");
} }
ObjectId proposal_id; ProposalId proposal_id;
std::string error; std::string error;
if (!get_proposal_id_from_path( if (!get_proposal_id_from_path(
ctx.rpc_ctx->get_request_path_params(), proposal_id, error)) ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
@ -1505,7 +1548,7 @@ namespace ccf
} }
std::string error; std::string error;
ObjectId proposal_id; ProposalId proposal_id;
if (!get_proposal_id_from_path( if (!get_proposal_id_from_path(
ctx.rpc_ctx->get_request_path_params(), proposal_id, error)) ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
{ {
@ -1967,7 +2010,11 @@ namespace ccf
} }
if (!set_jwt_public_signing_keys( if (!set_jwt_public_signing_keys(
ctx.tx, INVALID_ID, parsed.issuer, issuer_metadata, parsed.jwks)) ctx.tx,
INVALID_PROPOSAL_ID,
parsed.issuer,
issuer_metadata,
parsed.jwks))
{ {
LOG_FAIL_FMT( LOG_FAIL_FMT(
"JWT key auto-refresh: error while storing signing keys for issuer " "JWT key auto-refresh: error while storing signing keys for issuer "

Просмотреть файл

@ -169,7 +169,9 @@ auto frontend_process(
} }
auto get_proposal( auto get_proposal(
MemberRpcFrontend& frontend, size_t proposal_id, const tls::Pem& caller) MemberRpcFrontend& frontend,
const ProposalId& proposal_id,
const tls::Pem& caller)
{ {
const auto getter = const auto getter =
create_request(nullptr, fmt::format("proposals/{}", proposal_id), HTTP_GET); create_request(nullptr, fmt::format("proposals/{}", proposal_id), HTTP_GET);
@ -180,7 +182,7 @@ auto get_proposal(
auto get_vote( auto get_vote(
MemberRpcFrontend& frontend, MemberRpcFrontend& frontend,
size_t proposal_id, ProposalId proposal_id,
MemberId voter, MemberId voter,
const tls::Pem& caller) const tls::Pem& caller)
{ {
@ -366,7 +368,7 @@ DOCTEST_TEST_CASE("Proposer ballot")
MemberRpcFrontend frontend(network, node, share_manager); MemberRpcFrontend frontend(network, node, share_manager);
frontend.open(); frontend.open();
size_t proposal_id; ProposalId proposal_id;
const ccf::Script vote_for("return true"); const ccf::Script vote_for("return true");
const ccf::Script vote_against("return false"); const ccf::Script vote_against("return false");
@ -468,7 +470,7 @@ DOCTEST_TEST_CASE("Reject duplicate vote")
MemberRpcFrontend frontend(network, node, share_manager); MemberRpcFrontend frontend(network, node, share_manager);
frontend.open(); frontend.open();
size_t proposal_id; ProposalId proposal_id;
const ccf::Script vote_for("return true"); const ccf::Script vote_for("return true");
const ccf::Script vote_against("return false"); const ccf::Script vote_against("return false");
@ -573,7 +575,6 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
auto i = 0ul; auto i = 0ul;
for (auto& new_member : new_members) for (auto& new_member : new_members)
{ {
const auto proposal_id = i;
new_member.id = initial_members + i++; new_member.id = initial_members + i++;
// new member certificate // new member certificate
@ -600,12 +601,13 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
const auto propose = const auto propose =
create_signed_request(proposal, "proposals", kp, member_cert); create_signed_request(proposal, "proposals", kp, member_cert);
ProposalId proposal_id;
{ {
const auto r = frontend_process(frontend, propose, member_cert); const auto r = frontend_process(frontend, propose, member_cert);
const auto result = parse_response_body<Propose::Out>(r); const auto result = parse_response_body<Propose::Out>(r);
// the proposal should be accepted, but not succeed immediately // the proposal should be accepted, but not succeed immediately
DOCTEST_CHECK(result.proposal_id == proposal_id); proposal_id = result.proposal_id;
DOCTEST_CHECK(result.state == ProposalState::OPEN); DOCTEST_CHECK(result.state == ProposalState::OPEN);
} }
@ -801,7 +803,7 @@ DOCTEST_TEST_CASE("Accept node")
} }
// m0 proposes adding new node // m0 proposes adding new node
ObjectId trust_node_proposal_id; ProposalId trust_node_proposal_id;
{ {
Script proposal(R"xxx( Script proposal(R"xxx(
local tables, node_id = ... local tables, node_id = ...
@ -855,7 +857,7 @@ DOCTEST_TEST_CASE("Accept node")
} }
// m0 proposes retire node // m0 proposes retire node
ObjectId retire_node_proposal_id; ProposalId retire_node_proposal_id;
{ {
Script proposal(R"xxx( Script proposal(R"xxx(
local tables, node_id = ... local tables, node_id = ...
@ -986,7 +988,7 @@ ProposalInfo test_raw_writes(
} }
// propose // propose
const auto proposal_id = 0ul; ProposalId proposal_id;
{ {
const uint8_t proposer_id = 0; const uint8_t proposer_id = 0;
const auto propose = const auto propose =
@ -997,7 +999,7 @@ ProposalInfo test_raw_writes(
const auto expected_state = const auto expected_state =
(n_members == 1) ? ProposalState::ACCEPTED : ProposalState::OPEN; (n_members == 1) ? ProposalState::ACCEPTED : ProposalState::OPEN;
DOCTEST_CHECK(r.state == expected_state); DOCTEST_CHECK(r.state == expected_state);
DOCTEST_CHECK(r.proposal_id == proposal_id); proposal_id = r.proposal_id;
if (r.state == ProposalState::ACCEPTED) if (r.state == ProposalState::ACCEPTED)
return r; return r;
} }
@ -1162,7 +1164,7 @@ DOCTEST_TEST_CASE("Remove proposal")
gen.finalize(); gen.finalize();
MemberRpcFrontend frontend(network, node, share_manager); MemberRpcFrontend frontend(network, node, share_manager);
frontend.open(); frontend.open();
auto proposal_id = 0; ProposalId proposal_id;
auto wrong_proposal_id = 1; auto wrong_proposal_id = 1;
ccf::Script proposal_script(R"xxx( ccf::Script proposal_script(R"xxx(
local tables, param = ... local tables, param = ...
@ -1182,7 +1184,7 @@ DOCTEST_TEST_CASE("Remove proposal")
const auto r = parse_response_body<Propose::Out>( const auto r = parse_response_body<Propose::Out>(
frontend_process(frontend, propose, member_cert)); frontend_process(frontend, propose, member_cert));
DOCTEST_CHECK(r.proposal_id == proposal_id); proposal_id = r.proposal_id;
DOCTEST_CHECK(r.state == ProposalState::OPEN); DOCTEST_CHECK(r.state == ProposalState::OPEN);
} }
@ -1294,7 +1296,7 @@ DOCTEST_TEST_CASE("Vetoed proposal gets rejected")
{ {
DOCTEST_INFO("Check proposal was rejected"); DOCTEST_INFO("Check proposal was rejected");
const auto proposal = get_proposal(frontend, 0, voter_a_cert); const auto proposal = get_proposal(frontend, r.proposal_id, voter_a_cert);
DOCTEST_CHECK(proposal.state == ProposalState::REJECTED); DOCTEST_CHECK(proposal.state == ProposalState::REJECTED);
} }
@ -1347,7 +1349,6 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
frontend_process(frontend, vote, member_cert)); frontend_process(frontend, vote, member_cert));
DOCTEST_CHECK(r.state == ProposalState::ACCEPTED); DOCTEST_CHECK(r.state == ProposalState::ACCEPTED);
DOCTEST_CHECK(r.proposal_id == 0);
auto tx1 = network.tables->create_tx(); auto tx1 = network.tables->create_tx();
const auto uid = tx1.get_view(network.values)->get(ValueIds::NEXT_USER_ID); const auto uid = tx1.get_view(network.values)->get(ValueIds::NEXT_USER_ID);
@ -1385,7 +1386,6 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
frontend_process(frontend, vote, member_cert)); frontend_process(frontend, vote, member_cert));
DOCTEST_CHECK(r.state == ProposalState::ACCEPTED); DOCTEST_CHECK(r.state == ProposalState::ACCEPTED);
DOCTEST_CHECK(r.proposal_id == 1);
auto tx1 = network.tables->create_tx(); auto tx1 = network.tables->create_tx();
auto user = tx1.get_view(network.users)->get(0); auto user = tx1.get_view(network.users)->get(0);
@ -1440,7 +1440,7 @@ DOCTEST_TEST_CASE(
MemberRpcFrontend frontend(network, node, share_manager); MemberRpcFrontend frontend(network, node, share_manager);
frontend.open(); frontend.open();
size_t proposal_id; ProposalId proposal_id;
size_t proposer_id = 1; size_t proposer_id = 1;
size_t voter_id = 2; size_t voter_id = 2;
@ -1578,7 +1578,7 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
MemberRpcFrontend frontend(network, node, share_manager); MemberRpcFrontend frontend(network, node, share_manager);
frontend.open(); frontend.open();
size_t proposal_id; ProposalId proposal_id;
const ccf::Script vote_for("return true"); const ccf::Script vote_for("return true");
const ccf::Script vote_against("return false"); const ccf::Script vote_against("return false");
@ -1759,7 +1759,7 @@ DOCTEST_TEST_CASE(
MemberRpcFrontend frontend(network, node, share_manager); MemberRpcFrontend frontend(network, node, share_manager);
frontend.open(); frontend.open();
size_t proposal_id; ProposalId proposal_id;
const ccf::Script vote_for("return true"); const ccf::Script vote_for("return true");
const ccf::Script vote_against("return false"); const ccf::Script vote_against("return false");

Просмотреть файл

@ -121,6 +121,21 @@ namespace tls
md_type); md_type);
} }
bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature,
mbedtls_md_type_t md_type,
HashBytes& hash_bytes) const
{
return verify(
contents.data(),
contents.size(),
signature.data(),
signature.size(),
md_type,
hash_bytes);
}
bool verify( bool verify(
const uint8_t* contents, const uint8_t* contents,
size_t contents_size, size_t contents_size,
@ -134,6 +149,18 @@ namespace tls
return verify_hash(hash, sig, sig_size, md_type); return verify_hash(hash, sig, sig_size, md_type);
} }
bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
mbedtls_md_type_t md_type,
HashBytes& hash_bytes) const
{
do_hash(cert->pk, contents, contents_size, hash_bytes, md_type);
return verify_hash(hash_bytes, sig, sig_size, md_type);
}
const mbedtls_x509_crt* raw() const mbedtls_x509_crt* raw()
{ {
return cert.get(); return cert.get();

Просмотреть файл

@ -269,7 +269,7 @@ class Consortium:
for proposal_id, attr in r.body.json().items(): for proposal_id, attr in r.body.json().items():
proposals.append( proposals.append(
infra.proposal.Proposal( infra.proposal.Proposal(
proposal_id=int(proposal_id), proposal_id=proposal_id,
proposer_id=int(attr["proposer"]), proposer_id=int(attr["proposer"]),
state=infra.proposal.ProposalState(attr["state"]), state=infra.proposal.ProposalState(attr["state"]),
) )