Derecho-Project / derecho

The main code repository for the Derecho project.
BSD 3-Clause "New" or "Revised" License
186 stars 47 forks source link

Add the option to create signed persistent logs #179

Closed etremel closed 4 years ago

etremel commented 4 years ago

This set of changes adds a new feature to Derecho: The ability to make nodes in a group sign each version of a persistent object and record this signature in the persistence log. All nodes in the group are expected to share the same private key, since they are all replicas of the same service; the signatures are for the purpose of proving to an external client that the Derecho group has "committed" to this update and cannot later claim to have a persistent log with different updates. The signature on each version of a replicated object covers both the data in that version and the signature of the previous version, so a signature is only valid if it is part of a chain of valid signatures going back to the first version of the object.

To add signatures to a persistent field in a replicated object, pass the optional parameter enable_signatures with value true to the Persistent<T> constructor. Then, make the replicated object inherit from the marker interface derecho::SignedPersistentFields rather than derecho::PersistsFields. Finally, ensure the config option PERS/private_key_file in derecho.cfg is set to a path to a private key in PEM format. An example of setting up a Derecho group with signed persistent objects is provided in the new test program applications/tests/unit_tests/signed_log_test.cpp.

When signatures are enabled, an additional callback is available in CallbackSet called global_verified_callback. This function takes the same parameters as the persistence callback (subgroup ID and version number), and it will be called when a new version of a subgroup's state has been persisted with a valid signature by all replicas in that subgroup. Each node will first persist its own update and then validate the other nodes' signatures on that update, so global_verified_callback should always occur after global_persistence_callback.

A node can also manually retrieve and verify the signature on any version of a persistent field by calling the methods Persistent::getSignature and Persistent::verify. However, note that there is one signature per replicated object, and a replicated object may have more than one persistent field, so each persistent field in the object will actually have the same signature for a given version. The methods PersistentRegistry::getSignature and PersistentRegistry::verify provide a consistent way of finding and verifying the single signature for a particular version of a replicated object, regardless of how many signed persistent fields it has.

When signatures are not enabled on any persistent field, this feature has no effect on Derecho and all persistent updates are delivered normally. @songweijia and I have thoroughly tested the performance of persistent updates on this branch with no signatures enabled (using persistent_bw_test.cpp) and found that there was no difference in throughput compared to the same tests being run on the "master" branch.