Adding New Members#

It is possible for existing members to add new members to the consortium after a CCF network has been started.

Generating Member Keys and Certificates#

Note

See Using Member Keys Stored in HSM for a guide on how to used member keys and certificate store in Azure Key Vault.

First, the identity and encryption public and private key pairs of the new member should be created.

The keygenerator.sh script can be used to generate the member’s certificate and associated private key as well as their encryption public and private keys. It is included in the ccf Python package, and the .deb package will install it under /opt/ccf_*/bin/.

$ keygenerator.sh --name member_name [--gen-enc-key]
-- Generating identity private key and certificate for participant "member_name"...
Identity curve: secp384r1
Identity private key generated at:   member_name_privk.pem
Identity certificate generated at:   member_name_cert.pem (to be registered in CCF)
# Only if --gen-enc-key is used:
-- Generating RSA encryption key pair for participant "member_name"...
Encryption private key generated at:  member_name_enc_privk.pem
Encryption public key generated at:   member_name_enc_pubk.pem (to be registered in CCF)

Members that are registered in CCF with a public encryption key are recovery members. Each recovery member is given a recovery share (see Submitting Recovery Shares) that can be used to recover a defunct service. Members registered without a public encryption key are not given recovery shares and cannot recover the defunct service.

The member’s identity and encryption private keys (e.g. member_name_privk.pem and member_name_enc_privk.pem) should be stored on a trusted device (e.g. HSM) while the certificate (e.g. member_name_cert.pem) and public encryption key (e.g. member_name_enc_pubk.pem) should be registered in CCF by members.

The CCF unique member identity is the hex-encoded string of the SHA-256 hash of the DER-encoded certificate, and can be computed from the certificate alone, without interacting with CCF:

$ identity_cert_path=/path/to/member/cert
$ openssl x509 -in "$identity_cert_path" -noout -fingerprint -sha256 | cut -d "=" -f 2 | sed 's/://g' | awk '{print tolower($0)}'
7f46110b62ccbbd5f18b4c9bda876024399fd538133f8c26d4bfe5a9d80e59e6

Note

See Algorithms and Curves for the list of supported cryptographic curves for member identity.

Registering a New Member#

Once the new member’s keys and certificate have been generated, the existing consortium should register the new member public information in CCF, following the usual propose and vote procedure.

The Submitting a New Proposal section describes the steps that members should follow to register a new member.

Activating a New Member#

Note

The ccf_cose_sign1 script is distributed in the ccf Python package, available on PyPI. It can be installed with pip install ccf.

A new member who gets registered in CCF is not yet able to participate in governance operations. To do so, the new member should first acknowledge that they are satisfied with the state of the service (for example, after auditing the current constitution and the nodes currently trusted).

First, the new member should update and retrieve the latest state digest via the POST /gov/members/state-digests/{memberId}:update endpoint. In doing so, the new member confirms that they are satisfied with the current state of the service.

$ touch empty_file
$ ccf_cose_sign1 \
  --ccf-gov-msg-type state_digest \
  --ccf-gov-msg-created_at `date -uIs` \
  --signing-key new_member_privk.pem \
  --signing-cert new_member_cert.pem \
  --content empty_file \ # Note that passing an empty file is required
| curl https://<ccf-node-address>/gov/members/state-digests/7f46110b62ccbbd5f18b4c9bda876024399fd538133f8c26d4bfe5a9d80e59e6:update?api-version=2023-06-01-preview \
  -X POST \
  --cacert service_cert.pem \
  --key new_member_privk.pem \
  --cert new_member_cert.pem \
  --silent | jq > request.json
$ cat request.json
{
    "digest": <...>
}

Then, the new member should sign the state digest returned by POST /gov/members/state-digests/{memberId}:update (or GET /gov/members/state-digests/{memberId}) via the POST /gov/members/state-digests/{memberId}:ack endpoint, using the ccf_cose_sign1 utility:

$ ccf_cose_sign1 \
  --ccf-gov-msg-type ack \
  --ccf-gov-msg-created_at `date -uIs` \
  --signing-key new_member_privk.pem \
  --signing-cert new_member_cert.pem \
  --content request.json \
| curl https://<ccf-node-address>/gov/members/state-digests/7f46110b62ccbbd5f18b4c9bda876024399fd538133f8c26d4bfe5a9d80e59e6:ack?api-version=2023-06-01-preview \
  --cacert service_cert.pem \
  --data-binary @- \
  -H "content-type: application/cose"

Once the command completes, the new member becomes active and can take part in governance operations (e.g. creating a new proposal or voting for an existing one). You can verify the activation of the member at GET /gov/service/members/{memberId}.

$ curl https://<ccf-node-address>/gov/service/members/7f46110b62ccbbd5f18b4c9bda876024399fd538133f8c26d4bfe5a9d80e59e6?api-version=2023-06-01-preview?api-version=2023-06-01-preview --silent | jq
{
    "memberId": "7f46110b62ccbbd5f18b4c9bda876024399fd538133f8c26d4bfe5a9d80e59e6",
    "certificate": <...>,
    "memberData": <...>,
    "status": "Active"
}

Note

The newly-activated member is also given a recovery share that can be used to recover a defunct service.