keri_core/event/sections/
key_config.rs

1use cesrox::primitives::CesrPrimitive;
2use said::{derivation::HashFunction, SelfAddressingIdentifier};
3use serde::{Deserialize, Serialize};
4
5use super::threshold::SignatureThreshold;
6use crate::{
7    database::redb::rkyv_adapter::said_wrapper::SaidValue,
8    prefix::{attached_signature::Index, BasicPrefix, IndexedSignature},
9};
10
11#[derive(
12    Serialize,
13    Deserialize,
14    Debug,
15    Clone,
16    Default,
17    PartialEq,
18    rkyv::Archive,
19    rkyv::Serialize,
20    rkyv::Deserialize,
21)]
22#[rkyv(derive(Debug))]
23
24pub struct NextKeysData {
25    #[serde(rename = "nt")]
26    pub threshold: SignatureThreshold,
27
28    #[serde(rename = "n")]
29    next_key_hashes: Vec<SaidValue>,
30}
31
32impl NextKeysData {
33    pub fn new(
34        threshold: SignatureThreshold,
35        next_keys_hashes: impl IntoIterator<Item = SelfAddressingIdentifier>,
36    ) -> Self {
37        let next_keys = next_keys_hashes
38            .into_iter()
39            .map(|said| said.into())
40            .collect();
41        Self {
42            threshold,
43            next_key_hashes: next_keys,
44        }
45    }
46
47    pub fn next_keys_hashes(&self) -> Vec<SelfAddressingIdentifier> {
48        self.next_key_hashes
49            .clone()
50            .into_iter()
51            .map(|said| said.into())
52            .collect()
53    }
54
55    /// Checks if next KeyConfig contains enough public keys to fulfill current
56    /// next threshold.
57    pub fn verify_next(&self, next: &KeyConfig) -> Result<bool, SignatureError> {
58        let indexes: Vec<_> = next
59            .public_keys
60            .iter()
61            .filter_map(|key| {
62                self.next_key_hashes
63                    .iter()
64                    .position(|dig| dig.said.verify_binding(key.to_str().as_bytes()))
65            })
66            .collect();
67
68        // check previous next threshold
69        self.threshold.enough_signatures(&indexes)?;
70        Ok(true)
71    }
72
73    /// Checks if public keys corresponding to signatures match keys committed in
74    /// NextKeysData and if it's enough of them
75    pub fn check_threshold<'a>(
76        &self,
77        public_keys: &[BasicPrefix],
78        indexes: impl IntoIterator<Item = &'a Index>,
79    ) -> Result<(), SignatureError> {
80        // Get indexes of keys in previous next key list.
81        let indexes_in_last_prev = self.matching_previous_indexes(public_keys, indexes);
82
83        // Check previous next threshold
84        self.threshold.enough_signatures(&indexes_in_last_prev)?;
85
86        Ok(())
87    }
88
89    /// Checks if hashes of public keys match public keys of provided indexes.
90    /// Returns list of positions in next keys list that matches.
91    fn matching_previous_indexes<'a>(
92        &self,
93        public_keys: &[BasicPrefix],
94        indexes: impl IntoIterator<Item = &'a Index>,
95    ) -> Vec<usize> {
96        // Get indexes of keys in previous next key list.
97        indexes
98            .into_iter()
99            .filter_map(|index| {
100                index.previous_next().and_then(|prev_next| {
101                    match (
102                        self.next_key_hashes.get(prev_next as usize),
103                        public_keys.get(index.current() as usize),
104                    ) {
105                        (Some(prev_next_digest), Some(current)) => prev_next_digest
106                            .said
107                            .verify_binding(current.to_str().as_bytes())
108                            .then_some(prev_next as usize),
109                        _ => None,
110                    }
111                })
112            })
113            .collect::<Vec<_>>()
114    }
115}
116
117#[derive(thiserror::Error, Debug, Serialize, Deserialize)]
118pub enum SignatureError {
119    #[error("Not enough signatures while verifying")]
120    NotEnoughSigsError,
121
122    #[error("Signature duplicate while verifying")]
123    DuplicateSignature,
124
125    #[error("Too many signatures while verifying")]
126    TooManySignatures,
127
128    #[error("Key index not present in the set")]
129    MissingIndex,
130
131    #[error("Wrong signature type error")]
132    WrongSignatureTypeError,
133
134    #[error("Wrong key type error")]
135    WrongKeyTypeError,
136}
137#[derive(
138    Serialize,
139    Deserialize,
140    Debug,
141    Clone,
142    Default,
143    PartialEq,
144    rkyv::Archive,
145    rkyv::Serialize,
146    rkyv::Deserialize,
147)]
148#[rkyv(derive(Debug))]
149pub struct KeyConfig {
150    #[serde(rename = "kt")]
151    pub threshold: SignatureThreshold,
152
153    #[serde(rename = "k")]
154    pub public_keys: Vec<BasicPrefix>,
155
156    #[serde(flatten)]
157    pub next_keys_data: NextKeysData,
158}
159
160impl KeyConfig {
161    pub fn new(
162        public_keys: Vec<BasicPrefix>,
163        next_keys_data: NextKeysData,
164        threshold: Option<SignatureThreshold>,
165    ) -> Self {
166        Self {
167            threshold: threshold.map_or_else(
168                || SignatureThreshold::Simple(public_keys.len() as u64 / 2 + 1),
169                |t| t,
170            ),
171            public_keys,
172            next_keys_data,
173        }
174    }
175
176    /// Verify
177    ///
178    /// Verifies the given sigs against the given message using the KeyConfigs
179    /// Public Keys, according to the indexes in the sigs.
180    pub fn verify(
181        &self,
182        message: &[u8],
183        sigs: &[IndexedSignature],
184    ) -> Result<bool, SignatureError> {
185        // there are no duplicates
186        if !(sigs
187            .iter()
188            .fold(vec![0u64; self.public_keys.len()], |mut acc, sig| {
189                acc[sig.index.current() as usize] += 1;
190                acc
191            })
192            .iter()
193            .all(|n| *n <= 1))
194        {
195            Err(SignatureError::DuplicateSignature.into())
196        } else if
197        // check if there are not too many
198        sigs.len() > self.public_keys.len() {
199            Err(SignatureError::TooManySignatures.into())
200
201        // ensure there's enough sigs
202        } else {
203            self.threshold.enough_signatures(
204                &sigs
205                    .iter()
206                    .map(|sig| sig.index.current() as usize)
207                    .collect::<Vec<_>>(),
208            )?;
209
210            sigs.iter()
211                .fold(Ok(true), |acc: Result<bool, SignatureError>, sig| {
212                    let verification_result: bool = self
213                        .public_keys
214                        .get(sig.index.current() as usize)
215                        .ok_or_else(|| SignatureError::from(SignatureError::MissingIndex))
216                        .and_then(|key: &BasicPrefix| Ok(key.verify(message, &sig.signature)?))?;
217                    Ok(acc? && verification_result)
218                })
219        }
220    }
221
222    /// Verify Next
223    ///
224    /// Verifies that the given next KeyConfig matches that which is committed
225    /// to in next_keys_data of this KeyConfig
226    pub fn verify_next(&self, next: &KeyConfig) -> Result<bool, SignatureError> {
227        self.next_keys_data.verify_next(next)
228    }
229
230    /// Serialize For Next
231    ///
232    /// Serializes the KeyConfig for creation or verification of a threshold
233    /// key digest commitment
234    pub fn commit(&self, derivation: &HashFunction) -> NextKeysData {
235        nxt_commitment(self.threshold.clone(), &self.public_keys, derivation)
236    }
237}
238
239/// Serialize For Commitment
240///
241/// Creates NextKeysData from given threshold and public keys set.
242pub fn nxt_commitment(
243    threshold: SignatureThreshold,
244    keys: &[BasicPrefix],
245    derivation: &HashFunction,
246) -> NextKeysData {
247    let next_key_hashes = keys
248        .iter()
249        .map(|bp| derivation.derive(bp.to_str().as_bytes()).into())
250        .collect();
251    NextKeysData {
252        threshold,
253        next_key_hashes,
254    }
255}
256
257#[cfg(test)]
258mod test {
259    use cesrox::{parse, primitives::CesrPrimitive};
260    use said::{derivation::HashFunction, derivation::HashFunctionCode};
261
262    use crate::{
263        error::Error,
264        event::sections::{
265            key_config::{nxt_commitment, NextKeysData, SaidValue, SignatureError},
266            threshold::SignatureThreshold,
267            KeyConfig,
268        },
269        prefix::{attached_signature::Index, BasicPrefix, IndexedSignature},
270    };
271
272    #[test]
273    fn test_next_commitment() {
274        // test data taken from keripy
275        // (keripy/tests/core/test_weighted_threshold.py::test_weighted)
276        // Set weighted threshold to [1/2, 1/2, 1/2]
277        let sith = SignatureThreshold::multi_weighted(vec![vec![(1, 2), (1, 2), (1, 2)]]);
278        let next_keys: Vec<BasicPrefix> = [
279            "DHqJ2DNmypwMKelWXLgl3V-9pDRcOenM5Wf03O1xx1Ri",
280            "DEIISiMvtnaPTpMHkoGs4d0JdbwjreW53OUBfMedLUaF",
281            "DDQFJ_uXcZum_DY6NNTtI5UrTEQo6PRWEANpn6hVtfyQ",
282        ]
283        .iter()
284        .map(|x| x.parse().unwrap())
285        .collect();
286        let nxt = nxt_commitment(sith, &next_keys, &HashFunctionCode::Blake3_256.into());
287
288        let threshold = SignatureThreshold::multi_weighted(vec![vec![(1, 2), (1, 2), (1, 2)]]);
289        let next_key_hashes: Vec<SaidValue> = [
290            "EFQZkN8MMEtZzaS-Tq1EEbH886vsf5SzwicSn_ywbzTy",
291            "ENOQnUj8GNr1ICJ1P4qmC3-aHTrpZqKVpZhvHCBVWE1p",
292            "EDFH1MfEJWlI9PpMbgBi_RGP7L4UivrLfozFucuEaWVH",
293        ]
294        .iter()
295        .map(|sai| SaidValue {
296            said: sai.parse().unwrap(),
297        })
298        .collect();
299
300        assert_eq!(
301            nxt,
302            NextKeysData {
303                threshold,
304                next_key_hashes,
305            }
306        );
307    }
308
309    #[test]
310    fn test_threshold() -> Result<(), Error> {
311        use ed25519_dalek::SigningKey;
312        use rand::rngs::OsRng;
313
314        use crate::{
315            keys::{PrivateKey, PublicKey},
316            prefix::SelfSigningPrefix,
317        };
318
319        let (pub_keys, priv_keys): (Vec<BasicPrefix>, Vec<PrivateKey>) = [0, 1, 2]
320            .iter()
321            .map(|_| {
322                let kp = SigningKey::generate(&mut OsRng);
323                (
324                    BasicPrefix::Ed25519(PublicKey::new(kp.verifying_key().to_bytes().to_vec())),
325                    PrivateKey::new(kp.to_bytes().to_vec()),
326                )
327            })
328            .unzip();
329        let current_threshold = SignatureThreshold::single_weighted(vec![(1, 4), (1, 2), (1, 2)]);
330
331        let next_key_hash = {
332            let next_threshold = SignatureThreshold::single_weighted(vec![(1, 2), (1, 2)]);
333            let next_keys: Vec<BasicPrefix> = [1, 2]
334                .iter()
335                .map(|_| {
336                    let kp = SigningKey::generate(&mut OsRng);
337                    BasicPrefix::Ed25519(PublicKey::new(kp.verifying_key().to_bytes().to_vec()))
338                })
339                .collect();
340            nxt_commitment(
341                next_threshold,
342                &next_keys,
343                &HashFunctionCode::Blake3_256.into(),
344            )
345        };
346        let key_config = KeyConfig::new(pub_keys, next_key_hash, Some(current_threshold));
347
348        let msg_to_sign = "message to signed".as_bytes();
349
350        let mut signatures = vec![];
351        for i in 0..priv_keys.len() {
352            let sig = priv_keys[i].sign_ed(msg_to_sign)?;
353            signatures.push(IndexedSignature::new_both_same(
354                SelfSigningPrefix::Ed25519Sha512(sig),
355                i as u16,
356            ));
357        }
358
359        // All signatures.
360        let st = key_config.verify(
361            msg_to_sign,
362            &vec![
363                signatures[0].clone(),
364                signatures[1].clone(),
365                signatures[2].clone(),
366            ],
367        );
368        // assert!(st.is_ok());
369        assert!(matches!(st, Ok(true)));
370
371        // Not enough signatures.
372        let st = key_config.verify(
373            msg_to_sign,
374            &vec![signatures[0].clone(), signatures[2].clone()],
375        );
376        assert!(matches!(st, Err(SignatureError::NotEnoughSigsError)));
377
378        // Enough signatures.
379        let st = key_config.verify(
380            msg_to_sign,
381            &vec![signatures[1].clone(), signatures[2].clone()],
382        );
383        assert!(st.is_ok());
384
385        // The same signatures.
386        let st = key_config.verify(
387            msg_to_sign,
388            &vec![
389                signatures[0].clone(),
390                signatures[0].clone(),
391                signatures[0].clone(),
392            ],
393        );
394        assert!(matches!(st, Err(SignatureError::DuplicateSignature)));
395
396        Ok(())
397    }
398
399    #[test]
400    fn test_verify() -> Result<(), Error> {
401        use std::convert::TryFrom;
402
403        use crate::{
404            event::event_data::EventData,
405            event_message::signed_event_message::{Message, Notice},
406        };
407
408        // test data taken from keripy
409        // (keripy/tests/core/test_weighted_threshold.py::test_weighted)
410        let ev = br#"{"v":"KERI10JSON000207_","t":"icp","d":"EIL2dvwm6lYAsyKKtzxIEFm51gSfwe3IIZSx8kI8ve7_","i":"EIL2dvwm6lYAsyKKtzxIEFm51gSfwe3IIZSx8kI8ve7_","s":"0","kt":["1/2","1/2","1/2"],"k":["DCuDiSPCTq-qBBFDHkhf1_kmysrH8KSsFvoaOSgEbx-X","DNUWS4GJHtBpn2Zvgh_ALFuB6E1OJvtphYLvJG8KfI0F","DAVcM7pvoz37lF1HBxFnaZQeGHKC9wVhlytEzKBfzXhV"],"nt":["1/2","1/2","1/2"],"n":["EFQZkN8MMEtZzaS-Tq1EEbH886vsf5SzwicSn_ywbzTy","ENOQnUj8GNr1ICJ1P4qmC3-aHTrpZqKVpZhvHCBVWE1p","EDFH1MfEJWlI9PpMbgBi_RGP7L4UivrLfozFucuEaWVH"],"bt":"0","b":[],"c":[],"a":[]}-AADAAC3xWTpnv14_khneBqDlrK7JHPUoHNJhWMIXzXbK80RVyEYV7iMsWaAXfepkRsyELBLd25atAtE3iLeDn1I-gUMABDr8iCcrun_otXsarVXpe6jgK2VG20RpgsVvFunUxHsrZRKm6gNjMAoKZkqzDVuY5tKD0XkTmonstex5Wj9dToBACAwNb8Lj-vxJYMi_vIH-ETGG0dVfqIk4ihrQvV1iL1_07eWfu4BwRYCPCZDo0F0Xbkz0DP4xXVfChR-lFd2npUG"#;
411        let parsed = parse(ev).unwrap().1;
412        let signed_msg = Message::try_from(parsed).unwrap();
413        match signed_msg {
414            Message::Notice(Notice::Event(ref e)) => {
415                if let EventData::Icp(icp) = e.to_owned().event_message.data.get_event_data() {
416                    let kc = icp.key_config;
417                    let msg = e.event_message.encode()?;
418                    assert!(kc.verify(&msg, &e.signatures).is_ok());
419                }
420            }
421            _ => (),
422        };
423
424        let ev  = br#"{"v":"KERI10JSON0002a6_","t":"rot","d":"EJ4TG5D0URQ0InD_EIDXDoI9v1y3vIk-0LMJMjeZXryh","i":"EIL2dvwm6lYAsyKKtzxIEFm51gSfwe3IIZSx8kI8ve7_","s":"3","p":"ELKSLVpbV9eH3xk2xBqH3fSgOmWTbUoBuE2JsLl0lu2L","kt":["1/2","1/2","1/2"],"k":["DO1ligy1cGMWDCwIw3HiBLOusTzGOAH88fkUMNsdJkMy","DJoOgGofKsiig4kMjV04ju9UCcIs42XxjOtQQPrNAORe","DEt34Sqbzwgy-VpnzePz5JiTDLEgnUU8RtuDkLn_xJh0"],"nt":[["1/2","1/2","1/2"],["1","1"]],"n":["ENzeDznmpi75oO8APbVzyW75xnmgLDJRo0rCHf4gsDPc","ELnNWeDypTMeaIZzbT8GoJJnbmm8ksJ8ic8b2-9KFZQK","ED2lFBwMbkNQy2vxFWLbbEg2V6OLChhLfTxmvuNGWz91","EHy3gn2wZog-q8V3r6RzduTN48nLEHgSYHaoNaWHrxrl","EHuCmMw5ksFOQxvDSXL9h-_94RMKERjqLj_KFSusuHQg"],"bt":"0","br":[],"ba":[],"a":[]}-AADAACxUM40kMP7aGrPIlwO1d6XAvk6jX22u2EwcB_IgsQSaxJlLbXEz4v2j9cUHQKkY7ek47TfFYir-rG5kyLWJa0MABCQ6AlObGVXjIslKCFZkZiBNvQSDLgUU_2sR4RQxghGCExNWG9jwsSAOFBGX5QcEb6Hqu4ZrdbnyV9GxRkR-jkDACC4Ydi6Jlqw9ROIqNvyHoXNoYcIZzI8iD8_YB1-U9J1xb55jG4z-1Ddyx8mLW6_O53boaFobaitvO13z3u5OswF"#;
425        let parsed = parse(ev).unwrap().1;
426        let signed_msg = Message::try_from(parsed).unwrap();
427        match signed_msg {
428            Message::Notice(Notice::Event(ref e)) => {
429                if let EventData::Icp(icp) = e.to_owned().event_message.data.get_event_data() {
430                    let kc = icp.key_config;
431                    let msg = e.event_message.encode()?;
432                    assert!(kc.verify(&msg, &e.signatures).is_ok());
433                }
434            }
435            _ => (),
436        };
437
438        Ok(())
439    }
440
441    #[test]
442    pub fn test_finding_matching_previous_indexes() -> Result<(), Error> {
443        use crate::signer::setup_signers;
444
445        let signers = setup_signers();
446        let hash_function: HashFunction = HashFunctionCode::Blake3_256.into();
447
448        let sample_public_keys: Vec<_> = signers
449            .iter()
450            .map(|bp| BasicPrefix::Ed25519(bp.public_key()))
451            .collect();
452        let sample_digests: Vec<_> = sample_public_keys
453            .clone()
454            .into_iter()
455            .map(|pk| hash_function.derive(pk.to_str().as_bytes()))
456            .collect();
457
458        let threshold = SignatureThreshold::single_weighted(vec![(1, 4), (1, 2), (1, 4), (1, 2)]);
459        let public_keys = sample_public_keys[..5].to_vec();
460        let initial_digests: Vec<_> = sample_digests[..5].to_vec();
461
462        // Corresponding previous next keys in the same order as in current keys.
463        let indexes = vec![Index::BothSame(0), Index::BothSame(1), Index::BothSame(2)];
464
465        let next_keys_data = NextKeysData::new(threshold.clone(), initial_digests.clone());
466        assert_eq!(
467            next_keys_data.matching_previous_indexes(&public_keys, &indexes[..2]),
468            vec![0, 1]
469        );
470        assert_eq!(
471            next_keys_data.matching_previous_indexes(&public_keys, &indexes),
472            vec![0, 1, 2]
473        );
474
475        // Corresponding previous next keys in other order than in current keys.
476        let indexes = vec![
477            Index::BothDifferent(0, 2),
478            Index::BothDifferent(1, 0),
479            Index::BothDifferent(2, 1),
480        ];
481        let digests = vec![
482            initial_digests[1].clone(),
483            initial_digests[2].clone(),
484            initial_digests[0].clone(),
485        ];
486
487        let next_keys_data = NextKeysData::new(threshold.clone(), digests.to_vec());
488        assert_eq!(
489            next_keys_data.matching_previous_indexes(&public_keys, &indexes[..2]),
490            vec![2, 0]
491        );
492        assert_eq!(
493            next_keys_data.matching_previous_indexes(&public_keys, &indexes),
494            vec![2, 0, 1]
495        );
496
497        // Some keys current only
498        let indexes = vec![
499            Index::CurrentOnly(0),
500            Index::BothDifferent(1, 2),
501            Index::BothDifferent(2, 1),
502        ];
503        let digests = vec![
504            initial_digests[0].clone(),
505            initial_digests[2].clone(),
506            initial_digests[1].clone(),
507        ];
508
509        let next_keys_data = NextKeysData::new(threshold.clone(), digests.to_vec());
510        assert_eq!(
511            next_keys_data.matching_previous_indexes(&public_keys, &indexes[..2]),
512            vec![2]
513        );
514        assert_eq!(
515            next_keys_data.matching_previous_indexes(&public_keys, &indexes),
516            vec![2, 1]
517        );
518
519        // Bad digest
520        let indexes = vec![
521            Index::CurrentOnly(0),
522            Index::BothDifferent(1, 2),
523            Index::BothDifferent(2, 1),
524        ];
525        let digests = vec![
526            initial_digests[0].clone(),
527            initial_digests[2].clone(),
528            hash_function.derive("Bad digest".as_bytes()),
529        ];
530
531        let next_keys_data = NextKeysData::new(threshold.clone(), digests.to_vec());
532        assert_eq!(
533            next_keys_data.matching_previous_indexes(&public_keys, &indexes),
534            vec![1]
535        );
536
537        Ok(())
538    }
539}