Skip to content

Use hash of node's public key as unique node identifier #2241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 41 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
285720c
WIP
Feb 23, 2021
c07447e
Compiles and starts one node
Feb 23, 2021
f8a4da7
e2e test with one node works
Feb 23, 2021
5c741ca
WIP
Feb 24, 2021
5b22112
Initial startup works with 3 nodes
Feb 25, 2021
5d19d05
Forwarding works
Feb 25, 2021
b6756ed
e2e logging works
Feb 25, 2021
f464872
Fix election test
Feb 25, 2021
5363ec2
Most unit tests work
Feb 25, 2021
de06394
Fix memberclient
Feb 25, 2021
5da3c1f
Fix reconfig test
Feb 25, 2021
56a1aa6
Little bit of cleanup
Feb 25, 2021
14845b2
Merge remote-tracking branch 'upstream/main' into node_id_hash_pubkey
Feb 26, 2021
fe2fb76
Merge remote-tracking branch 'upstream/main' into node_id_hash_pubkey
Mar 2, 2021
128307b
Fix most of progress tracker unit test
Mar 2, 2021
84a4175
Fix issues with BFT
Mar 2, 2021
1fdf6d4
Some cleanup
Mar 2, 2021
8cf816a
Fix sandbox
Mar 2, 2021
33e81b2
Fix governance test
Mar 2, 2021
b28e053
Merge remote-tracking branch 'upstream/main' into node_id_hash_pubkey
Mar 2, 2021
5a0797f
Fix raft unit tests
Mar 2, 2021
885e87f
Fix governance test
Mar 2, 2021
795eeae
fmt
Mar 2, 2021
481ae2a
Merge branch 'main' into node_id_hash_pubkey
jumaffre Mar 3, 2021
a835128
Big refactor
Mar 3, 2021
544ddfe
Merge branch 'node_id_hash_pubkey' of github.com:jumaffre/CCF into no…
Mar 3, 2021
ad46c55
Merge remote-tracking branch 'upstream/main' into node_id_hash_pubkey
Mar 3, 2021
121d732
Shorten number of characters to print in node's log
Mar 3, 2021
c136259
fmt
Mar 3, 2021
6ab3322
Docs
Mar 3, 2021
c4e4981
Update CHANGELOG.md
jumaffre Mar 3, 2021
0b7ed01
Use DER public key
Mar 4, 2021
44e0709
verify_quote.sh
Mar 4, 2021
5e9d2e3
Merge branch 'node_id_hash_pubkey' of github.com:jumaffre/CCF into no…
Mar 4, 2021
8fc7ce0
const ref
Mar 4, 2021
6e567a0
namespace kv::test
Mar 4, 2021
cb54007
Remove aft::NodeId
Mar 4, 2021
d8c58cd
Merge remote-tracking branch 'upstream/main' into node_id_hash_pubkey
Mar 4, 2021
e02a499
fmt
Mar 4, 2021
9c3f2fb
Do not enforce node ID format
Mar 4, 2021
61f7820
Update python/utils/verify_quote.sh
jumaffre Mar 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed

- Node unique identifier is the hex-encoded string of the SHA-256 digest of the node's identity public key (#2241).

## [0.18.5]

### Changed
Expand Down
2 changes: 1 addition & 1 deletion doc/build_apps/run_app.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Additionally, if snapshots were generated by the defunct service (using the ``--
[16:24:29.563] - Ledger: 0.ledger
[16:24:29.563] No available snapshot to recover from. Entire transaction history will be replayed.
[16:24:32.885] Started CCF network with the following nodes:
[16:24:32.885] Node [1] = https://127.0.0.1:8000
[16:24:32.885] Node [0] = https://127.0.0.1:8000
[16:24:32.885] You can now issue business transactions to the liblogging.enclave.so.signed application.
[16:24:32.885] Keys and certificates have been copied to the common folder: ./workspace/sandbox_common/
[16:24:32.885] See https://microsoft.github.io/CCF/main/use_apps/issue_commands.html for more information.
Expand Down
6 changes: 3 additions & 3 deletions doc/governance/common_member_operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Trusting a New Node

As opposed to an opening network in which nodes are trusted automatically, new nodes added to an open network must become trusted through a governance proposal and vote before becoming part of the network.

When an operator starts a new node with the ``join`` option (see :ref:`operations/start_network:Adding a New Node to the Network`), the joining node is assigned a unique node id and is recorded in state `PENDING`. Then, members can vote to accept the new node, using the unique assigned node id (see :ref:`governance/proposals:Proposing and Voting for a Proposal` for more detail).
When an operator starts a new node with the ``join`` option (see :ref:`operations/start_network:Adding a New Node to the Network`), the node is recorded in state ``PENDING``. Then, members can vote to accept the new node, using the unique node id (hex-encoded string of the SHA-256 digest of the node's identity public key). See :ref:`governance/proposals:Proposing and Voting for a Proposal` for more detail.

Once the proposal successfully completes, the new node automatically becomes part of the network.

.. note:: Once trusted, it may take some time for the new node to update its ledger and replay the transactions run on the network before it joined.
.. note:: Once trusted, it may take some time for the new node to update its ledger and replay the transactions run on the network before it joined (from the beginning of time, or from the snapshot it started from).

Updating Code Version
---------------------
Expand Down Expand Up @@ -122,4 +122,4 @@ The number of member shares required to restore the private ledger (``recovery_t
"state": "ACCEPTED"
}

.. note:: The new recovery threshold has to be in the range between 1 and the current number of active members.
.. note:: The new recovery threshold has to be in the range between 1 and the current number of active recovery members.
2 changes: 2 additions & 0 deletions doc/operations/start_network.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ CCF nodes can be started by using IP Addresses (both IPv4 and IPv6 are supported

When starting up, the node generates its own key pair and outputs the certificate associated with its public key at the location specified by ``--node-cert-file``. The certificate of the freshly-created CCF network is also output at the location specified by ``--network-cert-file``.

The unique identifier of a CCF node is the hex-encoded string of the SHA-256 digest the public key contained in its identity certificate (e.g. ``50211327a77fc16dd2fba8fae5fffac3df909fceeb307cf804a4125ae2679007``). This unique identifier should be used by operators and members to refer to this node with CCF (for example, when :ref:`governance/common_member_operations:Trusting a New Node`).

.. note:: The network certificate should be distributed to users and members to be used as the certificate authority (CA) when establishing a TLS connection with any of the nodes part of the CCF network. When using ``curl``, this is passed as the ``--cacert`` argument.

The certificates, encryption public keys and member data of initial members of the consortium are specified via ``--member-info``. For example:
Expand Down
13 changes: 8 additions & 5 deletions doc/schemas/node_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"$ref": "#/components/schemas/int64"
},
"primary_id": {
"$ref": "#/components/schemas/uint64"
"$ref": "#/components/schemas/NodeId"
},
"service_status": {
"$ref": "#/components/schemas/ServiceStatus"
Expand All @@ -131,7 +131,6 @@
"required": [
"service_status",
"current_view",
"primary_id",
"view_change_in_progress"
],
"type": "object"
Expand All @@ -148,7 +147,7 @@
"$ref": "#/components/schemas/string"
},
"node_id": {
"$ref": "#/components/schemas/uint64"
"$ref": "#/components/schemas/NodeId"
},
"port": {
"$ref": "#/components/schemas/string"
Expand Down Expand Up @@ -219,7 +218,7 @@
"$ref": "#/components/schemas/int64"
},
"node_id": {
"$ref": "#/components/schemas/uint64"
"$ref": "#/components/schemas/NodeId"
},
"recovery_target_seqno": {
"$ref": "#/components/schemas/int64"
Expand Down Expand Up @@ -273,6 +272,10 @@
],
"type": "object"
},
"NodeId": {
"pattern": "^[a-f0-9]{64}$",
"type": "string"
},
"NodeStatus": {
"enum": [
"PENDING",
Expand All @@ -293,7 +296,7 @@
"$ref": "#/components/schemas/string"
},
"node_id": {
"$ref": "#/components/schemas/uint64"
"$ref": "#/components/schemas/NodeId"
},
"raw": {
"$ref": "#/components/schemas/string"
Expand Down
10 changes: 7 additions & 3 deletions python/ccf/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ def _read(self):
if map_name in self._msgpacked_tables:
k = msgpack.unpackb(k, **UNPACK_ARGS)
val = msgpack.unpackb(val, **UNPACK_ARGS)
if map_name == NODES_TABLE_NAME:
k = str(k)
records[k] = val

remove_count = self._read_next()
Expand Down Expand Up @@ -254,9 +256,11 @@ def add_transaction(self, transaction):
for nodeid, values in signature_table.items():
current_seqno = values[self.EXPECTED_NODE_SEQNO_INDEX]
current_view = values[self.EXPECTED_NODE_VIEW_INDEX]
signing_node = values[self.EXPECTED_NODE_SIGNATURE_INDEX][
self.EXPECTED_SIGNING_NODE_ID_INDEX
]
signing_node = str(
values[self.EXPECTED_NODE_SIGNATURE_INDEX][
self.EXPECTED_SIGNING_NODE_ID_INDEX
]
)

# Get binary representations for the cert, existing root, and signature
cert = b"".join(self.node_certificates[signing_node])
Expand Down
4 changes: 2 additions & 2 deletions python/ccf/proposal_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,12 @@ def read_modules(modules_path: str) -> List[dict]:


@cli_proposal
def trust_node(node_id: int, **kwargs):
def trust_node(node_id: str, **kwargs):
return build_proposal("trust_node", node_id, **kwargs)


@cli_proposal
def retire_node(node_id: int, **kwargs):
def retire_node(node_id: str, **kwargs):
return build_proposal("retire_node", node_id, **kwargs)


Expand Down
20 changes: 6 additions & 14 deletions python/utils/verify_quote.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,18 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the Apache 2.0 License.

set -e
set -ex

quote_file_name="quote.bin"
endorsements_file_name="endorsements.bin"
open_enclave_path=${OPEN_ENCLAVE_PATH:-"/opt/openenclave"}

# Re-enable once oeverify displays custom claims
# See https://github.com/openenclave/openenclave/pull/3817
if [ -z "${CCF_VERIFY_QUOTED_CERT}" ]; then
verify_quoted_cert=false
else
verify_quoted_cert=true
fi

function usage()
{
echo "Usage:"
echo " $0 https://<node-address> [--mrenclave <mrenclave_hex>] [CURL_OPTIONS]"
echo "Verify target node's remote attestation quote."
echo "Verification involves confirming that the public key of the node certificate matches the SGX report data and that the mrenclave included in the quote is trusted."
echo "Verification involves confirming that the public key (DER encoded) of the node certificate matches the SGX report data and that the mrenclave included in the quote is trusted."
echo "A specific trusted mrenclave can be specified with --mrenclave. If specified, the quoted mrenclave must match this exactly. If unspecified, the service's currently accepted code versions will be retrieved from the target node, and verification will succeed only if the quoted mrenclave is present in this list."
}

Expand Down Expand Up @@ -81,7 +73,7 @@ if [ ! -s "${tmp_dir}/${quote_file_name}" ]; then
exit 1
fi

echo "Node quote successfully retrieved. Verifying quote..."
echo "Node quote successfully retrieved."

oeverify_output=$("${open_enclave_path}"/bin/oeverify -r "${tmp_dir}"/"${quote_file_name}" -e "${tmp_dir}"/"${endorsements_file_name}")

Expand All @@ -90,9 +82,9 @@ oeverify_report_data=$(echo "${oeverify_output}" | grep "sgx_report_data" | cut
# Extract hex sha-256 (64 char) from report data (128 char)
extracted_report_data=$(echo "${oeverify_report_data#*0x}" | head -c 64)

# Remove protocol and compute hash of target node's public key
# Remove protocol and compute hash of target node's public key (DER)
stripped_node_address=${node_address#*//}
node_pubk_hash=$(echo | openssl s_client -showcerts -connect "${stripped_node_address}" 2>/dev/null | openssl x509 -pubkey -noout | sha256sum | awk '{ print $1 }')
node_pubk_hash=$(echo | openssl s_client -showcerts -connect "${stripped_node_address}" 2>/dev/null | openssl x509 -pubkey -noout | openssl ec -pubin -outform der 2>/dev/null | sha256sum | awk '{ print $1 }')

# Extract mrenclave
is_mrenclave_valid=false
Expand All @@ -104,7 +96,7 @@ for mrenclave in "${trusted_mrenclaves[@]}"; do
fi
done

if [ "${verify_quoted_cert}" = true ] && [ "${extracted_report_data}" != "${node_pubk_hash}" ]; then
if [ "${extracted_report_data}" != "${node_pubk_hash}" ]; then
echo "Error: quote verification failed."
echo "Reported quote data does not match node certificate public key:"
echo "\"${extracted_report_data}\" != \"${node_pubk_hash}\""
Expand Down
Loading