1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! Key manager state in the consensus layer.
use std::collections::HashMap;

use anyhow::Result;
use thiserror::Error;

use crate::{
    common::{
        crypto::{
            hash::Hash,
            signature::{PublicKey, SignatureBundle},
        },
        namespace::Namespace,
        sgx::EnclaveIdentity,
    },
    consensus::beacon::EpochTime,
};

/// Context used to sign key manager CHURP policies.
const POLICY_SIGNATURE_CONTEXT: &[u8] = b"oasis-core/keymanager/churp: policy";

/// Errors emitted by the CHURP module.
#[derive(Error, Debug)]
pub enum Error {
    #[error("invalid signature")]
    InvalidSignature,
}

/// Cipher suite identifier.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Decode, cbor::Encode)]
#[repr(u8)]
pub enum SuiteId {
    /// 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.
    #[default]
    NistP384Sha3_384 = 0,
}

/// Status represents the current state of a CHURP instance.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Decode, cbor::Encode)]
pub struct Status {
    /// A unique identifier within the key manager runtime.
    pub id: u8,

    /// The identifier of the key manager runtime.
    pub runtime_id: Namespace,

    /// The identifier of a cipher suite used for verifiable secret sharing
    /// and key derivation.
    pub suite_id: SuiteId,

    /// 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.
    pub threshold: u8,

    /// 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.
    pub extra_shares: u8,

    /// The time interval in epochs between handoffs.
    ///
    /// A zero value disables handoffs.
    pub handoff_interval: EpochTime,

    /// A signed SGX access control policy.
    pub policy: SignedPolicySGX,

    /// 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.
    pub handoff: EpochTime,

    /// The hash of the verification matrix from the last successfully completed
    /// handoff.
    #[cbor(optional)]
    pub checksum: Option<Hash>,

    /// 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.
    #[cbor(optional)]
    pub committee: Vec<PublicKey>,

    /// 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.
    pub next_handoff: EpochTime,

    /// 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.
    #[cbor(optional)]
    pub next_checksum: Option<Hash>,

    /// 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.
    #[cbor(optional)]
    pub applications: HashMap<PublicKey, Application>,
}

/// Application represents a node's application to form a new committee.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Decode, cbor::Encode)]
pub struct Application {
    /// The hash of the random verification matrix.
    ///
    /// In all handoffs, except in the dealer phase, the verification matrix
    /// needs to be zero-hole.
    pub checksum: Hash,

    /// Reconstructed is true if and only if the node verified all matrices
    /// and successfully reconstructed its share during the handoff.
    pub reconstructed: bool,
}

/// Key manager access control policy.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct PolicySGX {
    /// A unique identifier within the key manager runtime.
    pub id: u8,

    /// The identifier of the key manager runtime.
    pub runtime_id: Namespace,

    /// A monotonically increasing policy serial number.
    pub serial: u32,

    /// A vector of enclave identities from which a share can be obtained
    /// during handouts.
    pub may_share: Vec<EnclaveIdentity>,

    /// A vector of enclave identities that may form the new committee
    /// in the next handoffs.
    pub may_join: Vec<EnclaveIdentity>,
}

/// Signed key manager access control policy.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct SignedPolicySGX {
    /// An SGX access control policy.
    pub policy: PolicySGX,

    /// A vector of signatures.
    #[cbor(optional)]
    pub signatures: Vec<SignatureBundle>,
}

impl SignedPolicySGX {
    /// Verify the signatures.
    pub fn verify(&self) -> Result<&PolicySGX> {
        let raw_policy = cbor::to_vec(self.policy.clone());
        for sig in &self.signatures {
            sig.signature
                .verify(&sig.public_key, POLICY_SIGNATURE_CONTEXT, &raw_policy)
                .map_err(|_| Error::InvalidSignature)?;
        }

        Ok(&self.policy)
    }
}