ADR 0023: Secret Sharing Schemes (CHURP)
Component
Oasis Core
Changelog
- 2025-07-15: Initial proposal
Status
Proposed
Context
Currently, key managers derive keys from either master secrets or ephemeral secrets, which are unique to the key manager runtime and shared among key manager nodes. We acknowledge that this approach is not comprehensive, as a compromise of a single key manager enclave would reveal past secrets, potentially leading to the decryption of the internal state of runtimes using them. However, this key derivation method is straightforward and, as a result, exceptionally fast.
While master secret rotations and ephemeral secrets aim to rotate the secrets to mitigate the impact of a secret compromise, we also want to support different types of key derivation, each offering varying levels of security. Examples include verifiable secret sharing schemes and dynamic-committee proactive secret sharing, where key managers would only hold a share of the secret.
This proposal aims to introduce support for the CHUrn-Robust Proactive secret sharing scheme (CHURP) and Key Derivation Center (KDC).
CHURP
CHURP is a proactive secret sharing scheme that allows the committee of nodes, each holding a share of the secret, to change dynamically over time. The CHURP protocol can be broadly divided into the following stages:
Setup
In this stage, the key manager owner configures CHURP. All steps in this stage can be performed off-chain.
-
The owner assigns unique, non-zero ID numbers to all nodes and associates them with their public keys. A simple approach is to encode each public key and use its binary representation as the node’s ID.
-
The owner selects a cipher suite based on security requirements, which determines the algebraic group used for cryptographic operations.
-
The owner also prepares an access control policy to specify which enclaves are trusted, and configures global parameters, such as how frequently key shares should be proactively refreshed, the minimum number of distinct shares required to reconstruct the secret, and the number of shares that can be lost before the secret becomes unrecoverable.
Initialization
Once the CHURP configuration is prepared, a new instance can be initialized.
-
The owner publishes the configuration in the consensus layer.
-
Key manager nodes that wish to participate update their configuration with the CHURP instance ID generated by consensus and then restart. To avoid requiring a restart, a CLI command could be added to support hot-loading of the updated configuration.
-
After restarting, each node requests its enclave to generate a non-zero-hole verification matrix for the upcoming dealing phase. The node then uses the checksum of this matrix to prepare an application to join the new committee and submits it to the consensus layer.
Dealing
The dealing starts the first epoch with a sufficient number of nodes that applied for the committee.
-
The first threshold plus two nodes serve as dealers; the rest are ignored. The consensus will discard the verification matrices from these nodes, and their entropy will not be included in the secret. However, they will still receive a share. The addition of two extra nodes prevents dealer corruption.
-
The construction of the shared secret and dealing occurs off-chain through a peer-to-peer network, following the specified enclave policy.
-
Each applied node (dealing):
-
requests its bivariate shares (polynomials and non-zero-hole verification matrices) from the dealers,
-
validates received shares,
-
verifies non-zero-hole verification matrices against the consensus layer,
-
combines shares (adds polynomials and merges non-zero-hole verification matrices),
-
seals the result (full share) and stores it locally in the enclave's confidential storage,
-
sends a transaction containing the checksum of the merged matrix to the consensus layer, confirming receipt of all shares.
-
-
If the timeout or epoch expires, or if checksums do not match, the dealing data in the consensus layer is cleared, and nodes must reapply for committee membership.
-
Upon receiving confirmations from all applied nodes, the consensus layer announces the new committee and begins collecting applications for the first handoff. Nodes may apply for the handoff no earlier than one epoch in advance.
-
The dealers delete the dealing data.
-
The committee starts serving requests.
Serving
Once committee nodes receive their shares, they begin serving requests for deriving key shares according to the KDC protocol, which clients can use to reconstruct the derived key.
-
To derive a key, a key manager client must contact at least the threshold number of committee nodes to obtain the required key shares.
-
The committee responds exclusively to nodes specified in the access policy.
-
Blame detection for corrupted key shares may be added later, though it is computational-intensive.
Handoff
Handoff transfers secret shares from the old committee to a new one. It occurs periodically, as defined in the CHURP configuration.
-
Starts if sufficient time has elapsed since the last handoff/dealing and an adequate number of nodes have prepared a zero-hole verification matrix and applied for the new committee.
-
Each applied node (share reduction):
-
requests switch data points for constructing the dimension switched polynomial and the merged verification matrix from the current committee,
-
validates received points,
-
verifies the merged verification matrix against the consensus layer,
-
combines the points into a polynomial (reduced share).
-
-
Each applied node (proactive randomization):
-
requests its bivariate shares (polynomials and zero-hole verification matrices) from the new committee members,
-
validates received shares,
-
verifies zero-hole verification matrices against the consensus layer,
-
applies shares to the secret polynomial and to the merged verification matrix.
-
-
Each applied node (full share distribution):
-
requests switch data points for constructing the dimension switched polynomial and the proactive verification matrix from the new committee members,
-
verifies received points,
-
combines the points into a polynomial (full share),
-
seals the result (full share) and stores it locally in the enclave's confidential storage,
-
sends a transaction containing the checksum of the proactive verification matrix to the consensus layer confirming that the full share was received.
-
-
If the committee hasn't changed, skip the share reduction and full node distribution steps, and only execute proactive randomization.
-
If the timeout or epoch expires, or if checksums do not match, the handoff data in the consensus layer is cleared, and nodes must reapply for committee membership.
-
Upon receiving confirmations from all applied nodes, the consensus layer announces the new committee and begins collecting applications for the next handoff.
-
The old committee deletes obsolete full shares.
-
The committee starts serving requests.
Key derivation center
Key derivation center (KDC) is a secret sharing scheme based on the verifiable secret sharing scheme (VSS) where every node possesses only a share of the master secret. To derive a key from the master secret, one needs to obtain at least threshold plus one number of key shares from distinct nodes and reassemble them locally.
Key manager applications
To facilitate the straightforward addition of new features to the key manager, we must first generalize it to support the concurrent execution of multiple (independent) applications.
Not all key manager runtimes are required to support all apps, and likewise, not all nodes need to run all the apps. In fact, the committee nodes for each application should be dynamic, allowing for the addition or removal of nodes based on specific requirements.
Current situation
Currently, the key manager supports two applications: one for generating, distributing, and storing master secrets, and the other for ephemeral secrets. However, these two applications are not independent, as they both share the same key manager policy for secret replication and key derivation.
Issues:
-
In the runtime, the logic for key manager status, policy, master secrets, and ephemeral secrets should be decoupled.
-
On the host, each application should have its own worker, e.g., a master secret app should have a dedicated worker responsible for participating in the master secret protocol.
Workers
Each key manager application should have a dedicated worker on the host node responsible for communicating with the app and ensuring its consensus view is up-to-date. For example, a master secret worker should be responsible for participating in the master secret protocol.
Handler trait
Each application should implement the following trait, which defines the enclave RPC methods exposed to the local host or remote clients. These methods are registered with the dispatcher during initialization.
/// RPC handler.
pub trait Handler {
/// Returns the list of RPC methods supported by this handler.
fn methods(&'static self) -> Vec<Method>;
}
Each application should adhere to the naming convention app.Method
.
Example 1
/// Master secrets key manager application.
pub trait MasterSecrets {
fn generate(&self);
fn load(&self);
fn replicate(&self);
fn key_pair(&self);
fn private_key(&self);
fn public_key(&self);
fn symmetric_key(&self);
fn update_status(&self);
}
Methods:
-
MasterSecrets.Generate
-
MasterSecrets.Load
-
MasterSecrets.Replicate
-
MasterSecrets.KeyPair
-
MasterSecrets.PrivateKey
-
MasterSecrets.PublicKey
-
MasterSecrets.SymmetricKey
-
MasterSecrets.UpdateStatus
Example 2
/// CPU change detection key manager application.
pub trait CPUChangeDetection {
fn encrypt(&self);
fn decrypt(&self);
}
Methods:
-
CpuChange.Encrypt
-
CpuChange.Decrypt
Example applications
Current and future applications:
-
Master secrets (for generation and replication master secrets)
-
Ephemeral secrets (for generation and replication of ephemeral secrets)
-
CPU change (for detecting whether the CPU has changed)
-
CHURP (secret sharing scheme)
-
Key derivation center (secret sharing scheme)
Implementation
This section outlines the core components used to implement the CHURP protocol. The implementation must support multiple secrets and remain resilient to key manager restarts. To meet these goals, the implementation must satisfy the following requirements:
-
Each group of nodes can share a unique secret.
-
Secrets may vary in terms of committee size, handoff intervals, security levels, etc.
-
State re-encryption should be possible with keys derived from a new shared secret.
-
Key manager restarts should not lose shares or committed data.
Identification
A node can be assigned a non-zero unique ID number based on its public key.
// NodeToID assigns a unique ID to a node for use in the CHURP protocol.
func NodeToID(nodeID signature.PublicKey) []byte {
id := nodeID
return id[:]
}
In addition to node identifiers, each CHURP instance can also be uniquely identified using the following structure:
// Identity uniquely identifies a CHURP instance.
type Identity struct {
// ID is a unique CHURP identifier within the key manager runtime.
ID uint8 `json:"id"`
// RuntimeID is the identifier of the key manager runtime.
RuntimeID common.Namespace `json:"runtime_id"`
}
Configuration
A CHURP instance can use one of several cipher suites designed for verifiable secret sharing and key derivation. Each cipher suite is assigned a unique identifier and must specify the following components:
-
An algebraic group used for cryptographic operations.
-
A hash function that maps data to elements within the group.
-
A hash function that maps data to scalars in the underlying field.
Initially, we should support only one suite that uses the SHA3 hash function and the NIST P-384 elliptic curve. Additional suites may be added in the future.
// NistP384Sha3_384 represents the NIST P-384 elliptic curve group with
// the SHA3-384 hash function used to encode arbitrary-length byte strings
// to elements of the underlying prime field or elliptic curve points.
const NistP384Sha3_384 uint8 = 0
To protect sensitive data, each enclave running the CHURP protocol enforces a strict access control policy. This policy defines which enclaves it is authorized to communicate with during various phases of the protocol.
// PolicySGX represents an SGX access control policy used to authenticate
// key manager enclaves during handoffs and remote client enclaves when
// querying key shares.
type PolicySGX struct {
Identity
// Serial is the monotonically increasing policy serial number.
Serial uint32 `json:"serial"`
// MayShare is the vector of enclave identities from which a share can be
// obtained during handoffs.
MayShare []sgx.EnclaveIdentity `json:"may_share"`
// MayJoin is the vector of enclave identities that may form the new
// committee in the next handoffs.
MayJoin []sgx.EnclaveIdentity `json:"may_join"`
// MayQuery is the map of runtime identities to the vector of enclave
// identities that may query key shares.
MayQuery map[common.Namespace][]sgx.EnclaveIdentity `json:"may_query,omitempty"`
}
Consensus transactions
The following method names define the CHURP-related transactions to be added to the consensus layer:
// MethodCreate is the method name for creating a new CHURP instance.
var MethodCreate = transaction.NewMethodName(ModuleName, "Create", CreateRequest{})
// CreateRequest contains the initial configuration.
type CreateRequest struct {
Identity
// SuiteID is the identifier of a cipher suite used for verifiable secret
// sharing and key derivation.
SuiteID uint8 `json:"suite_id,omitempty"`
// Threshold is the minimum number of distinct shares required
// to reconstruct a key.
Threshold uint8 `json:"threshold,omitempty"`
// ExtraShares represents the minimum number of shares that can be lost
// to render the secret unrecoverable.
ExtraShares uint8 `json:"extra_shares,omitempty"`
// HandoffInterval is the time interval in epochs between handoffs.
//
// A zero value disables handoffs.
HandoffInterval beacon.EpochTime `json:"handoff_interval,omitempty"`
// Policy is a signed SGX access control policy.
Policy SignedPolicySGX `json:"policy,omitempty"`
}
// MethodUpdate is the method name for CHURP updates.
var MethodUpdate = transaction.NewMethodName(ModuleName, "Update", UpdateRequest{})
// UpdateRequest contains the updated configuration.
type UpdateRequest struct {
Identity
// ExtraShares represents the minimum number of shares that can be lost
// to render the secret unrecoverable.
ExtraShares *uint8 `json:"extra_shares,omitempty"`
// HandoffInterval is the time interval in epochs between handoffs.
//
// Zero value disables handoffs.
HandoffInterval *beacon.EpochTime `json:"handoff_interval,omitempty"`
// Policy is a signed SGX access control policy.
Policy *SignedPolicySGX `json:"policy,omitempty"`
}
// MethodApply is the method name for a node submitting an application
// to form a new committee.
var MethodApply = transaction.NewMethodName(ModuleName, "Apply", ApplicationRequest{})
// ApplicationRequest contains node's application to form a new committee.
type ApplicationRequest struct {
// Identity of the CHRUP scheme.
Identity
// Epoch is the epoch of the handoff for which the node would like
// to apply.
Epoch beacon.EpochTime `json:"epoch"`
// Checksum is the hash of the verification matrix.
Checksum hash.Hash `json:"checksum"`
}
// MethodConfirm is the method name for a node confirming completion
// of a handoff.
var MethodConfirm = transaction.NewMethodName(ModuleName, "Confirm", ConfirmationRequest{})
// ConfirmationRequest confirms that the node successfully completed
// the handoff.
type ConfirmationRequest struct {
Identity
// Epoch is the epoch of the handoff for which the node reconstructed
// the share.
Epoch beacon.EpochTime `json:"epoch"`
// Checksum is the hash of the verification matrix.
Checksum hash.Hash `json:"checksum"`
}
Churp status
The CHURP status provides a comprehensive overview of the selected instance. It includes information such as how frequently handoffs occur, when the next handoff is scheduled, which nodes have submitted applications for the upcoming handoff, and which nodes form the current committee.
// Status represents the current state of a CHURP instance.
type Status struct {
Identity
// SuiteID is the identifier of a cipher suite used for verifiable secret
// sharing and key derivation.
SuiteID uint8 `json:"suite_id"`
// Threshold represents the degree of the secret-sharing polynomial.
//
// In a (t,n) secret-sharing scheme, where t represents the threshold,
// any combination of t+1 or more shares can reconstruct the secret,
// while losing n-t or fewer shares still allows the secret to be
// recovered.
Threshold uint8 `json:"threshold"`
// ExtraShares represents the minimum number of shares that can be lost
// to render the secret unrecoverable.
//
// If t and e represent the threshold and extra shares, respectively,
// then the minimum size of the committee is t+e+1.
ExtraShares uint8 `json:"extra_shares"`
// HandoffInterval is the time interval in epochs between handoffs.
//
// A zero value disables handoffs.
HandoffInterval beacon.EpochTime `json:"handoff_interval"`
// Policy is a signed SGX access control policy.
Policy SignedPolicySGX `json:"policy"`
// Handoff is the epoch of the last successfully completed handoff.
//
// The zero value indicates that no handoffs have been completed so far.
// Note that the first handoff is special and is called the dealer phase,
// in which nodes do not reshare or randomize shares but instead construct
// the secret and shares.
Handoff beacon.EpochTime `json:"handoff"`
// The hash of the verification matrix from the last successfully completed
// handoff.
Checksum *hash.Hash `json:"checksum,omitempty"`
// Committee is a vector of nodes holding a share of the secret
// in the active handoff.
//
// A client needs to obtain more than a threshold number of key shares
// from the nodes in this vector to construct the key.
Committee []signature.PublicKey `json:"committee,omitempty"`
// NextHandoff defines the epoch in which the next handoff will occur.
//
// If an insufficient number of applications is received, the next handoff
// will be delayed by one epoch.
NextHandoff beacon.EpochTime `json:"next_handoff"`
// NextChecksum is the hash of the verification matrix from the current
// handoff.
//
// The first candidate to confirm share reconstruction is the source
// of truth for the checksum. All other candidates need to confirm
// with the same checksum; otherwise, the applications will be annulled,
// and the nodes will need to apply for the new committee again.
NextChecksum *hash.Hash `json:"next_checksum,omitempty"`
// Applications is a map of nodes that wish to form the new committee.
//
// Candidates are expected to generate a random bivariate polynomial,
// construct a verification matrix, compute its checksum, and submit
// an application one epoch in advance of the next scheduled handoff.
// Subsequently, upon the arrival of the handoff epoch, nodes must execute
// the handoff protocol and confirm the reconstruction of its share.
Applications map[signature.PublicKey]Application `json:"applications,omitempty"`
}
Key manager worker
Enabling CHURP on a key manager node requires explicitly specifying the CHURP instances it should join.
// Config is the keymanager worker configuration structure.
type Config struct {
// ... existing fields omitted ...
// Churp holds configuration details for the CHURP extension.
Churp ChurpConfig `yaml:"churp,omitempty"`
}
// ChurpConfig holds configuration details for the CHURP extension.
type ChurpConfig struct {
// Schemes is a list of CHURP scheme configurations.
Schemes []ChurpSchemeConfig `yaml:"schemes,omitempty"`
}
// ChurpSchemeConfig holds configuration details for a CHURP scheme.
type ChurpSchemeConfig struct {
// ID is the unique identifier of the CHURP scheme.
ID uint8 `yaml:"id,omitempty"`
}
CHURP application
The application should be capable of running multiple CHURP instances, with each instance implementing the following trait:
/// Interface for handling a CHURP instance.
pub(crate) trait Handler: Send + Sync {
/// Returns the verification matrix of the shared secret bivariate
/// polynomial from the last successfully completed handoff.
///
/// The verification matrix is a matrix of dimensions t_n x t_m, where
/// t_n = threshold and t_m = 2 * threshold + 1. It contains encrypted
/// coefficients of the secret bivariate polynomial whose zero coefficient
/// represents the shared secret.
///
/// Verification matrix:
/// ```text
/// M = [b_{i,j} * G]
/// ```
/// Bivariate polynomial:
/// ```text
/// B(x,y) = \sum_{i=0}^{t_n} \sum_{j=0}^{t_m} b_{i,j} x^i y^j
/// ```
/// Shared secret:
/// ```text
/// Secret = B(0, 0)
/// ```
///
/// This matrix is used to verify switch points derived from the bivariate
/// polynomial share in handoffs.
///
/// NOTE: This method can be called over an insecure channel, as the matrix
/// does not contain any sensitive information. However, the checksum
/// of the matrix should always be verified against the consensus layer.
fn verification_matrix(&self, req: &QueryRequest) -> Result<Vec<u8>>;
/// Returns switch point for share reduction for the calling node.
///
/// The point is evaluation of the shared secret bivariate polynomial
/// at the given x (me) and y value (node ID).
///
/// Switch point:
/// ```text
/// Point = B(me, node_id)
/// ```
/// Bivariate polynomial:
/// ```text
/// B(x,y) = \sum_{i=0}^{t_n} \sum_{j=0}^{t_m} b_{i,j} x^i y^j
/// ```
///
/// WARNING: This method must be called over a secure channel as the point
/// needs to be kept secret and generated only for authorized nodes.
fn share_reduction_switch_point(&self, ctx: &RpcContext, req: &QueryRequest)
-> Result<Vec<u8>>;
/// Returns switch point for full share distribution for the calling node.
///
/// The point is evaluation of the proactivized shared secret bivariate
/// polynomial at the given x (node ID) and y value (me).
///
/// Switch point:
/// ```text
/// Point = B(node_id, me) + \sum Q_i(node_id, me)
/// ```
/// Bivariate polynomial:
/// ```text
/// B(x,y) = \sum_{i=0}^{t_n} \sum_{j=0}^{t_m} b_{i,j} x^i y^j
/// ```
/// Proactive bivariate polynomial:
/// ```text
/// Q_i(x,y) = \sum_{i=0}^{t_n} \sum_{j=0}^{t_m} b_{i,j} x^i y^j
/// ```
///
/// WARNING: This method must be called over a secure channel as the point
/// needs to be kept secret and generated only for authorized nodes.
fn share_distribution_switch_point(
&self,
ctx: &RpcContext,
req: &QueryRequest,
) -> Result<Vec<u8>>;
/// Returns proactive bivariate polynomial share for the calling node.
///
/// A bivariate share is a partial evaluation of a randomly selected
/// bivariate polynomial at a specified x or y value (node ID). Each node
/// interested in joining the new committee selects a bivariate polynomial
/// before the next handoff and commits to it by submitting the checksum
/// of the corresponding verification matrix to the consensus layer.
/// The latter can be used to verify the received bivariate shares.
///
/// Bivariate polynomial share:
/// ```text
/// S_i(y) = Q_i(node_id, y) (dealing phase or unchanged committee)
/// S_i(x) = Q_i(x, node_id) (committee changes)
/// ```
/// Proactive bivariate polynomial:
/// ```text
/// Q_i(x,y) = \sum_{i=0}^{t_n} \sum_{j=0}^{t_m} b_{i,j} x^i y^j
/// ```
///
/// WARNING: This method must be called over a secure channel as
/// the polynomial needs to be kept secret and generated only
/// for authorized nodes.
fn bivariate_share(
&self,
ctx: &RpcContext,
req: &QueryRequest,
) -> Result<EncodedVerifiableSecretShare>;
/// Returns the key share for the given key ID generated by the key
/// derivation center.
///
/// Key share:
/// ```text
/// KS_i = s_i * H(key_id)
/// ```
///
/// WARNING: This method must be called over a secure channel as the key
/// share needs to be kept secret and generated only for authorized nodes.
fn sgx_policy_key_share(
&self,
ctx: &RpcContext,
req: &KeyShareRequest,
) -> Result<EncodedEncryptedPoint>;
/// Prepare CHURP for participation in the given handoff of the protocol.
///
/// Initialization randomly selects a bivariate polynomial for the given
/// handoff, computes the corresponding verification matrix and its
/// checksum, and signs the latter.
///
/// Bivariate polynomial:
/// B(x,y) = \sum_{i=0}^{t_n} \sum_{j=0}^{t_m} b_{i,j} x^i y^j
///
/// Verification matrix:
/// M = [b_{i,j} * G]
///
/// Checksum:
/// H = KMAC256(M, runtime ID, handoff)
///
/// The bivariate polynomial is zero-hole in all handoffs expect in the
/// first one (dealing phase).
///
/// This method must be called locally.
fn apply(&self, req: &HandoffRequest) -> Result<SignedApplicationRequest>;
/// Tries to fetch switch points for share reduction from the given nodes.
///
/// Switch points should be obtained from (at least) t distinct nodes
/// belonging to the old committee, verified against verification matrix
/// whose checksum was published in the consensus layer, merged into
/// a reduced share using Lagrange interpolation and proactivized with
/// bivariate shares.
///
/// Switch point:
/// ```text
/// P_i = B(node_i, me)
///```
/// Reduced share:
/// ```text
/// RS(x) = B(x, me)
/// ````
/// Proactive reduced share:
/// ```text
/// QR(x) = RS(x) + \sum Q_i(x, me)
/// ````
fn share_reduction(&self, req: &FetchRequest) -> Result<FetchResponse>;
/// Tries to fetch switch data points for full share distribution from
/// the given nodes.
///
/// Switch points should be obtained from (at least) 2t distinct nodes
/// belonging to the new committee, verified against the sum of the
/// verification matrix and the verification matrices of proactive
/// bivariate shares, whose checksums were published in the consensus
/// layer, and merged into a full share using Lagrange interpolation.
///
/// Switch point:
/// ```text
/// P_i = B(me, node_i) + \sum Q_i(me, node_i)
///```
/// Full share:
/// ```text
/// FS(x) = B(me, y) + \sum Q_i(me, y) = B'(me, y)
/// ````
fn share_distribution(&self, req: &FetchRequest) -> Result<FetchResponse>;
/// Tries to fetch proactive bivariate shares from the given nodes.
///
/// Bivariate shares should be fetched from all candidates for the new
/// committee, including our own, verified against verification matrices
/// whose checksums were published in the consensus layer, and summed
/// into a bivariate polynomial.
///
/// Bivariate polynomial share:
/// ```text
/// S_i(y) = Q_i(me, y) (dealing phase or unchanged committee)
/// S_i(x) = Q_i(x, me) (committee changes)
/// ```
fn proactivization(&self, req: &FetchRequest) -> Result<FetchResponse>;
/// Returns a signed confirmation request containing the checksum
/// of the merged verification matrix.
fn confirmation(&self, req: &HandoffRequest) -> Result<SignedConfirmationRequest>;
/// Finalizes the specified scheme by cleaning up obsolete dealers,
/// handoffs, and shareholders. If the handoff was just completed,
/// the shareholder is made available, and its share is persisted
/// to the local storage.
fn finalize(&self, req: &HandoffRequest) -> Result<()>;
}
Methods:
-
Churp.Apply
-
Churp.ShareReduction
-
Churp.ShareDistribution
-
Churp.Proactivization
-
Churp.Confirm
-
Churp.Finalize
-
Churp.VerificationMatrix
-
Churp.ShareReductionPoint
-
Churp.ShareDistributionPoint
-
Churp.BivariateShare
-
Churp.SGXPolicyKeyShare
Key manager client
The key manager client should be extended to support CHURP functionality.
/// Key manager client interface.
#[async_trait]
pub trait KeyManagerClient: Send + Sync {
/// ... existing fields omitted ...
/// Returns the verification matrix for the given handoff.
async fn churp_verification_matrix(
&self,
churp_id: u8,
epoch: EpochTime,
nodes: Vec<PublicKey>,
) -> Result<Vec<u8>, KeyManagerError>;
/// Returns a switch point for the share reduction phase
/// of the given handoff.
async fn churp_share_reduction_point(
&self,
churp_id: u8,
epoch: EpochTime,
node_id: PublicKey,
nodes: Vec<PublicKey>,
) -> Result<Vec<u8>, KeyManagerError>;
/// Returns a switch point for the share distribution phase
/// of the given handoff.
async fn churp_share_distribution_point(
&self,
churp_id: u8,
epoch: EpochTime,
node_id: PublicKey,
nodes: Vec<PublicKey>,
) -> Result<Vec<u8>, KeyManagerError>;
/// Returns a bivariate share for the given handoff.
async fn churp_bivariate_share(
&self,
churp_id: u8,
epoch: EpochTime,
node_id: PublicKey,
nodes: Vec<PublicKey>,
) -> Result<EncodedVerifiableSecretShare, KeyManagerError>;
/// Returns state key.
async fn churp_state_key(
&self,
churp_id: u8,
key_id: KeyPairId,
) -> Result<StateKey, KeyManagerError>;
}
Key derivation center application
The key derivation center must implement the following traits to support share generation and key recovery.
/// A trait for shareholders capable of deriving key shares.
pub trait KeySharer<G: Group> {
/// Derives a key share based on the given key ID and domain separation tag.
fn make_key_share<H: GroupDigest<Output = G>>(
&self,
key_id: &[u8],
dst: &[u8],
) -> Result<EncryptedPoint<G>>;
}
/// A trait for recovering a secret key from key shares.
pub trait KeyRecoverer {
/// Returns the minimum number of key shares required to recover
/// the secret key.
fn min_shares(&self) -> usize;
/// Recovers the secret key from the provided key shares.
fn recover_key<G>(&self, shares: &[EncryptedPoint<G>]) -> Result<G>
where
G: Group + Zeroize;
}
Consequences
Positive
CHURP:
-
High security, as the master secret is shared among key manager nodes.
-
Supports proactive randomization (share refresh).
-
Dynamic committees.
KDC:
-
High security, as the master secret is shared among key manager nodes.
-
Supports proactive randomization (share refresh).
Negative
CHURP:
- Handoffs are computationally intensive.
KDC:
-
The number of key manager nodes that share a master secret is fixed and cannot be changed once shares are generated. Consequently, if too many nodes are destroyed, the secret cannot be recovered.
-
Support for replicating a share to a specific node is needed.
Neutral
- Issuing derived key shares with CHURP should be slightly slower compared to KDC.