From 15d6a680c6c2f54d8df0666d408ca2520d9952e6 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 27 Oct 2023 16:31:11 +0000 Subject: [PATCH] feat(crypto): CRP-2228 Add support at the crypto component layer for P-256 threshold ECDSA --- rs/consensus/src/ecdsa/payload_builder.rs | 10 +- rs/consensus/src/ecdsa/utils.rs | 10 +- .../src/key_id/tests.rs | 4 + .../src/vault/local_csp_vault/tecdsa/tests.rs | 3 +- .../src/sign/canister_threshold_sig/ecdsa.rs | 21 +- .../canister_threshold_sig/ecdsa/tests.rs | 138 +- .../idkg/complaint/tests.rs | 744 +-- .../idkg/dealing/tests.rs | 163 +- .../src/dummy_values.rs | 5 +- .../canister_threshold_sigs/src/lib.rs | 6 +- rs/crypto/tests/canister_threshold_sigs.rs | 4962 ++++++++--------- rs/types/types/src/crypto.rs | 10 + .../src/crypto/canister_threshold_sig.rs | 9 + .../src/crypto/canister_threshold_sig/idkg.rs | 7 +- rs/types/types/src/crypto/tests.rs | 11 + 15 files changed, 3096 insertions(+), 3007 deletions(-) diff --git a/rs/consensus/src/ecdsa/payload_builder.rs b/rs/consensus/src/ecdsa/payload_builder.rs index 73c01bef471..d15cb763a28 100644 --- a/rs/consensus/src/ecdsa/payload_builder.rs +++ b/rs/consensus/src/ecdsa/payload_builder.rs @@ -1706,7 +1706,10 @@ mod tests { ); payload.key_transcript.next_in_creation = ecdsa::KeyTranscriptCreation::XnetReshareOfUnmaskedParams(( - Box::new(dummy_initial_idkg_dealing_for_tests(&mut rng)), + Box::new(dummy_initial_idkg_dealing_for_tests( + AlgorithmId::ThresholdEcdsaSecp256k1, + &mut rng, + )), params, )); let result = update_next_key_transcript( @@ -2621,7 +2624,10 @@ mod tests { }; // Generate initial dealings - let initial_dealings = dummy_initial_idkg_dealing_for_tests(&mut rng); + let initial_dealings = dummy_initial_idkg_dealing_for_tests( + AlgorithmId::ThresholdEcdsaSecp256k1, + &mut rng, + ); let init_tid = initial_dealings.params().transcript_id(); // Step 1: initial bootstrap payload should be created successfully diff --git a/rs/consensus/src/ecdsa/utils.rs b/rs/consensus/src/ecdsa/utils.rs index 610ef5dcea4..655f0d7cef3 100644 --- a/rs/consensus/src/ecdsa/utils.rs +++ b/rs/consensus/src/ecdsa/utils.rs @@ -451,7 +451,10 @@ mod tests { #[test] fn test_inspect_ecdsa_initializations_one_key() { let mut rng = reproducible_rng(); - let initial_dealings = dummy_initial_idkg_dealing_for_tests(&mut rng); + let initial_dealings = dummy_initial_idkg_dealing_for_tests( + ic_types::crypto::AlgorithmId::ThresholdEcdsaSecp256k1, + &mut rng, + ); let key_id = EcdsaKeyId::from_str("Secp256k1:some_key").unwrap(); let ecdsa_init = EcdsaInitialization { key_id: Some((&key_id).into()), @@ -467,7 +470,10 @@ mod tests { #[test] fn test_inspect_ecdsa_initializations_multiple_keys() { let mut rng = reproducible_rng(); - let initial_dealings = dummy_initial_idkg_dealing_for_tests(&mut rng); + let initial_dealings = dummy_initial_idkg_dealing_for_tests( + ic_types::crypto::AlgorithmId::ThresholdEcdsaSecp256k1, + &mut rng, + ); let key_id = EcdsaKeyId::from_str("Secp256k1:some_key").unwrap(); let key_id_2 = EcdsaKeyId::from_str("Secp256k1:some_key_2").unwrap(); let ecdsa_init = EcdsaInitialization { diff --git a/rs/crypto/internal/crypto_service_provider/src/key_id/tests.rs b/rs/crypto/internal/crypto_service_provider/src/key_id/tests.rs index 4aea54ddc3c..68f236f3a00 100644 --- a/rs/crypto/internal/crypto_service_provider/src/key_id/tests.rs +++ b/rs/crypto/internal/crypto_service_provider/src/key_id/tests.rs @@ -148,6 +148,10 @@ mod stability_tests { input: (AlgorithmId::MegaSecp256k1, bytes), expected: "93549663cba48293c1d9a92de585a49581e05af84563aecd47fb7ab5fe9745c3", }, + ParameterizedTest { + input: (AlgorithmId::ThresholdEcdsaSecp256r1, bytes), + expected: "001b0b2aa06c51280e5267b7c9a5c5aa1691dcec75622eaa5c30d2ed08c5f25a", + }, ]; for test in &tests { diff --git a/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/tecdsa/tests.rs b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/tecdsa/tests.rs index d6fae78530f..0c77bf122ba 100644 --- a/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/tecdsa/tests.rs +++ b/rs/crypto/internal/crypto_service_provider/src/vault/local_csp_vault/tecdsa/tests.rs @@ -176,8 +176,7 @@ mod ecdsa_sign_share { use strum::IntoEnumIterator; AlgorithmId::iter() - .filter(|algorithm_id| *algorithm_id != AlgorithmId::ThresholdEcdsaSecp256k1) - .filter(|algorithm_id| *algorithm_id != AlgorithmId::ThresholdEcdsaSecp256r1) + .filter(|algorithm_id| !algorithm_id.is_threshold_ecdsa()) .for_each(|wrong_algorithm_id| { let parameters = EcdsaSignShareParameters::default(); let mut canister_sks = MockSecretKeyStore::new(); diff --git a/rs/crypto/src/sign/canister_threshold_sig/ecdsa.rs b/rs/crypto/src/sign/canister_threshold_sig/ecdsa.rs index 995c4bd477c..e63834f4d8c 100644 --- a/rs/crypto/src/sign/canister_threshold_sig/ecdsa.rs +++ b/rs/crypto/src/sign/canister_threshold_sig/ecdsa.rs @@ -1,8 +1,8 @@ //! Implementations of ThresholdEcdsaSigner use ic_crypto_internal_csp::api::{CspThresholdEcdsaSigVerifier, CspThresholdEcdsaSigner}; use ic_crypto_internal_threshold_sig_ecdsa::{ - IDkgTranscriptInternal, ThresholdEcdsaCombinedSigInternal, ThresholdEcdsaSerializationError, - ThresholdEcdsaSigShareInternal, + EccCurveType, IDkgTranscriptInternal, ThresholdEcdsaCombinedSigInternal, + ThresholdEcdsaSerializationError, ThresholdEcdsaSigShareInternal, }; use ic_logger::{info, ReplicaLogger}; use ic_types::crypto::canister_threshold_sig::error::{ @@ -29,8 +29,12 @@ fn get_tecdsa_master_public_key_from_internal_transcript( idkg_transcript_internal: &IDkgTranscriptInternal, ) -> MasterEcdsaPublicKey { let pub_key = idkg_transcript_internal.constant_term(); + let alg = match pub_key.curve_type() { + EccCurveType::K256 => AlgorithmId::EcdsaSecp256k1, + EccCurveType::P256 => AlgorithmId::EcdsaP256, + }; MasterEcdsaPublicKey { - algorithm_id: AlgorithmId::EcdsaSecp256k1, + algorithm_id: alg, public_key: pub_key.serialize(), } } @@ -230,8 +234,8 @@ pub enum MasterPublicKeyExtractionError { pub fn get_tecdsa_master_public_key( idkg_transcript: &IDkgTranscript, ) -> Result { - match idkg_transcript.algorithm_id { - AlgorithmId::ThresholdEcdsaSecp256k1 => match idkg_transcript.transcript_type { + if idkg_transcript.algorithm_id.is_threshold_ecdsa() { + match idkg_transcript.transcript_type { Unmasked(_) => { let internal_transcript = IDkgTranscriptInternal::try_from(idkg_transcript) .map_err(|e| { @@ -242,10 +246,11 @@ pub fn get_tecdsa_master_public_key( )) } Masked(_) => Err(MasterPublicKeyExtractionError::CannotExtractFromMasked), - }, - _ => Err(MasterPublicKeyExtractionError::UnsupportedAlgorithm( + } + } else { + Err(MasterPublicKeyExtractionError::UnsupportedAlgorithm( format!("{:?}", idkg_transcript.algorithm_id), - )), + )) } } diff --git a/rs/crypto/src/sign/canister_threshold_sig/ecdsa/tests.rs b/rs/crypto/src/sign/canister_threshold_sig/ecdsa/tests.rs index 69b823716d9..6c92ce2c0b5 100644 --- a/rs/crypto/src/sign/canister_threshold_sig/ecdsa/tests.rs +++ b/rs/crypto/src/sign/canister_threshold_sig/ecdsa/tests.rs @@ -22,46 +22,50 @@ mod get_tecdsa_master_public_key { #[test] fn should_return_error_if_transcript_type_is_masked() { - let transcript = dummy_transcript( - IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - AlgorithmId::ThresholdEcdsaSecp256k1, - vec![], - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let transcript = dummy_transcript( + IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + alg, + vec![], + ); - assert_matches!( - get_tecdsa_master_public_key(&transcript), - Err(MasterPublicKeyExtractionError::CannotExtractFromMasked) - ); + assert_matches!( + get_tecdsa_master_public_key(&transcript), + Err(MasterPublicKeyExtractionError::CannotExtractFromMasked) + ); + } } #[test] fn should_return_error_if_internal_transcript_cannot_be_deserialized() { - let transcript = dummy_transcript( - IDkgTranscriptType::Unmasked(IDkgUnmaskedTranscriptOrigin::ReshareUnmasked( - dummy_transcript_id(), - )), - AlgorithmId::ThresholdEcdsaSecp256k1, - vec![], - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let transcript = dummy_transcript( + IDkgTranscriptType::Unmasked(IDkgUnmaskedTranscriptOrigin::ReshareUnmasked( + dummy_transcript_id(), + )), + alg, + vec![], + ); - assert_matches!( - get_tecdsa_master_public_key(&transcript), - Err(MasterPublicKeyExtractionError::SerializationError( error )) - if error.contains("SerializationError") - ); + assert_matches!( + get_tecdsa_master_public_key(&transcript), + Err(MasterPublicKeyExtractionError::SerializationError( error )) + if error.contains("SerializationError") + ); + } } #[test] fn should_return_error_if_algorithm_id_is_invalid() { AlgorithmId::iter() - .filter(|algorithm_id| *algorithm_id != AlgorithmId::ThresholdEcdsaSecp256k1) + .filter(|algorithm_id| !algorithm_id.is_threshold_ecdsa()) .for_each(|wrong_algorithm_id| { let transcript = dummy_transcript( IDkgTranscriptType::Unmasked(IDkgUnmaskedTranscriptOrigin::ReshareUnmasked( dummy_transcript_id(), )), wrong_algorithm_id, - valid_internal_transcript_raw() + valid_internal_transcript_raw(AlgorithmId::ThresholdEcdsaSecp256k1) .serialize() .expect("serialization of internal transcript raw should succeed"), ); @@ -75,47 +79,77 @@ mod get_tecdsa_master_public_key { #[test] fn should_return_master_ecdsa_public_key() { - let transcript = dummy_transcript( - IDkgTranscriptType::Unmasked(IDkgUnmaskedTranscriptOrigin::ReshareUnmasked( - dummy_transcript_id(), - )), - AlgorithmId::ThresholdEcdsaSecp256k1, - valid_internal_transcript_raw() - .serialize() - .expect("serialization of internal transcript raw should succeed"), - ); - let expected_valid_master_ecdsa_public_key = valid_master_ecdsa_public_key(); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let transcript = dummy_transcript( + IDkgTranscriptType::Unmasked(IDkgUnmaskedTranscriptOrigin::ReshareUnmasked( + dummy_transcript_id(), + )), + alg, + valid_internal_transcript_raw(alg) + .serialize() + .expect("serialization of internal transcript raw should succeed"), + ); + let expected_valid_master_ecdsa_public_key = valid_master_ecdsa_public_key(alg); - assert_matches!( - get_tecdsa_master_public_key(&transcript), - Ok(tecdsa_master_public_key) - if tecdsa_master_public_key == expected_valid_master_ecdsa_public_key - ); + assert_matches!( + get_tecdsa_master_public_key(&transcript), + Ok(tecdsa_master_public_key) + if tecdsa_master_public_key == expected_valid_master_ecdsa_public_key + ); + } } /// Retrieved from a successful execution of /// `ic_crypto_internal_threshold_sig_ecdsa::transcript::new`. - const VALID_INTERNAL_TRANSCRIPT_RAW: &str = + const VALID_SECP256K1_INTERNAL_TRANSCRIPT_RAW: &str = "a173636f6d62696e65645f636f6d6d69746d656e74a16b427953756d6d6174696f\ 6ea168506564657273656ea166706f696e7473825822010252a937b4c129d822412\ d79f39d3626f32e7a1cf85ba1dfb01c9671d7d434003f582201025b168f9f47284b\ ed02b26197840033de1668d53ef8f4d6928b61cc7efec2a838"; - fn valid_internal_transcript_raw() -> IDkgTranscriptInternal { - IDkgTranscriptInternal::deserialize( - &hex::decode(VALID_INTERNAL_TRANSCRIPT_RAW) - .expect("hex decoding of valid internal transcript raw should succeed"), - ) - .expect("deserialization of valid internal transcript raw bytes should succeed") - } + const VALID_SECP256R1_INTERNAL_TRANSCRIPT_RAW: &str = + "a173636f6d62696e65645f636f6d6d69746d656e74a16b427953756d6d6174696f\ + 6ea168506564657273656ea166706f696e7473825822020279474d9bb87dce85dc\ + fc0786c9b4a4ddcb662e36fd716c42a0781fa05d208afb58220203915ca5584abf\ + 0abd9e71fb68561d607a96c61bf621c8092d7ea00677f5324829"; - fn valid_master_ecdsa_public_key() -> MasterEcdsaPublicKey { - MasterEcdsaPublicKey { - algorithm_id: AlgorithmId::EcdsaSecp256k1, - public_key: hex::decode( - "0252a937b4c129d822412d79f39d3626f32e7a1cf85ba1dfb01c9671d7d434003f", + fn valid_internal_transcript_raw(alg: AlgorithmId) -> IDkgTranscriptInternal { + match alg { + AlgorithmId::ThresholdEcdsaSecp256k1 => IDkgTranscriptInternal::deserialize( + &hex::decode(VALID_SECP256K1_INTERNAL_TRANSCRIPT_RAW) + .expect("hex decoding of valid internal transcript raw should succeed"), + ) + .expect("deserialization of valid internal transcript raw bytes should succeed"), + AlgorithmId::ThresholdEcdsaSecp256r1 => IDkgTranscriptInternal::deserialize( + &hex::decode(VALID_SECP256R1_INTERNAL_TRANSCRIPT_RAW) + .expect("hex decoding of valid internal transcript raw should succeed"), ) - .expect("hex decoding of public key bytes should succeed"), + .expect("deserialization of valid internal transcript raw bytes should succeed"), + unexpected => { + panic!("Unexpected threshold ECDSA algorithm {}", unexpected); + } + } + } + + fn valid_master_ecdsa_public_key(alg: AlgorithmId) -> MasterEcdsaPublicKey { + match alg { + AlgorithmId::ThresholdEcdsaSecp256k1 => MasterEcdsaPublicKey { + algorithm_id: AlgorithmId::EcdsaSecp256k1, + public_key: hex::decode( + "0252a937b4c129d822412d79f39d3626f32e7a1cf85ba1dfb01c9671d7d434003f", + ) + .expect("hex decoding of public key bytes should succeed"), + }, + AlgorithmId::ThresholdEcdsaSecp256r1 => MasterEcdsaPublicKey { + algorithm_id: AlgorithmId::EcdsaP256, + public_key: hex::decode( + "0279474d9bb87dce85dcfc0786c9b4a4ddcb662e36fd716c42a0781fa05d208afb", + ) + .expect("hex decoding of public key bytes should succeed"), + }, + unexpected => { + panic!("Unexpected threshold ECDSA algorithm {}", unexpected); + } } } diff --git a/rs/crypto/src/sign/canister_threshold_sig/idkg/complaint/tests.rs b/rs/crypto/src/sign/canister_threshold_sig/idkg/complaint/tests.rs index bc8457f45a7..99e5c0b9ded 100644 --- a/rs/crypto/src/sign/canister_threshold_sig/idkg/complaint/tests.rs +++ b/rs/crypto/src/sign/canister_threshold_sig/idkg/complaint/tests.rs @@ -32,61 +32,65 @@ fn should_call_csp_with_correct_arguments() { const COMPLAINER: NodeId = NODE_4; const DEALER: NodeId = NODE_2; let rng = &mut reproducible_rng(); - let complainer_key = generate_mega_public_key(rng); - let internal_complaint_raw = valid_internal_complaint_raw(); - let internal_dealing_raw = valid_internal_dealing_raw(); - let dealer_index = 2; - let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert( - dealer_index, - batch_signed_dealing_with(internal_dealing_raw.clone(), DEALER), - ); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1, NODE_2, NODE_3, NODE_4])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complainer_index = 3; // index of COMPLAINER in transcript.receivers - let complaint = IDkgComplaint { - transcript_id, - dealer_id: DEALER, - internal_complaint_raw: internal_complaint_raw.clone(), - }; - let registry = registry_with(mega_encryption_pk_record_with_key( - &complainer_key, - COMPLAINER, - REG_V1, - )); - let internal_complaint = IDkgComplaintInternal::deserialize(&internal_complaint_raw).unwrap(); - let internal_dealing = IDkgDealingInternal::deserialize(&internal_dealing_raw).unwrap(); - let context_data = transcript.context_data(); - - let mut csp = MockAllCryptoServiceProvider::new(); - csp.expect_idkg_verify_complaint() - .withf( - move |internal_complaint_, - complainer_index_, - complainer_key_, - internal_dealing_, - dealer_index_, - context_data_| { - *internal_complaint_ == internal_complaint - && *complainer_index_ == complainer_index - && *complainer_key_ == complainer_key - && *internal_dealing_ == internal_dealing - && *dealer_index_ == dealer_index - && *context_data_ == context_data - }, - ) - .times(1) - .return_const(Ok(())); - let _ = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, COMPLAINER); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let complainer_key = generate_mega_public_key(rng); + let internal_complaint_raw = valid_internal_complaint_raw(); + let internal_dealing_raw = valid_internal_dealing_raw(); + let dealer_index = 2; + let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert( + dealer_index, + batch_signed_dealing_with(internal_dealing_raw.clone(), DEALER), + ); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1, NODE_2, NODE_3, NODE_4])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complainer_index = 3; // index of COMPLAINER in transcript.receivers + let complaint = IDkgComplaint { + transcript_id, + dealer_id: DEALER, + internal_complaint_raw: internal_complaint_raw.clone(), + }; + let registry = registry_with(mega_encryption_pk_record_with_key( + &complainer_key, + COMPLAINER, + REG_V1, + )); + let internal_complaint = + IDkgComplaintInternal::deserialize(&internal_complaint_raw).unwrap(); + let internal_dealing = IDkgDealingInternal::deserialize(&internal_dealing_raw).unwrap(); + let context_data = transcript.context_data(); + + let mut csp = MockAllCryptoServiceProvider::new(); + csp.expect_idkg_verify_complaint() + .withf( + move |internal_complaint_, + complainer_index_, + complainer_key_, + internal_dealing_, + dealer_index_, + context_data_| { + *internal_complaint_ == internal_complaint + && *complainer_index_ == complainer_index + && *complainer_key_ == complainer_key + && *internal_dealing_ == internal_dealing + && *dealer_index_ == dealer_index + && *context_data_ == context_data + }, + ) + .times(1) + .return_const(Ok(())); + + let _ = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, COMPLAINER); + } } #[test] @@ -96,62 +100,67 @@ fn should_fail_on_transcript_id_mismatch() { let transcript_id_2 = transcript_id_1.increment(); assert_ne!(transcript_id_1, transcript_id_2); - let transcript = IDkgTranscript { - transcript_id: transcript_id_1, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings: BTreeMap::new(), - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id: transcript_id_2, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - let csp = MockAllCryptoServiceProvider::new(); - let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); - - let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); - - assert_matches!( - result, - Err(IDkgVerifyComplaintError::InvalidArgumentMismatchingTranscriptIDs) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let transcript = IDkgTranscript { + transcript_id: transcript_id_1, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings: BTreeMap::new(), + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id: transcript_id_2, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + let csp = MockAllCryptoServiceProvider::new(); + let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); + + let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::InvalidArgumentMismatchingTranscriptIDs) + ); + } } #[test] fn should_fail_if_dealing_missing_in_transcript() { const COMPLAINT_DEALER_ID: NodeId = NODE_2; let rng = &mut reproducible_rng(); - let verified_dealings_missing_complaint_dealer_id = BTreeMap::new(); - - let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings: verified_dealings_missing_complaint_dealer_id, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: COMPLAINT_DEALER_ID, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - let csp = MockAllCryptoServiceProvider::new(); - let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); - - let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); - assert_matches!( - result, - Err(IDkgVerifyComplaintError::InvalidArgumentMissingDealingInTranscript { dealer_id }) - if dealer_id == COMPLAINT_DEALER_ID - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let verified_dealings_missing_complaint_dealer_id = BTreeMap::new(); + + let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings: verified_dealings_missing_complaint_dealer_id, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: COMPLAINT_DEALER_ID, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + let csp = MockAllCryptoServiceProvider::new(); + let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); + + let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::InvalidArgumentMissingDealingInTranscript { dealer_id }) + if dealer_id == COMPLAINT_DEALER_ID + ); + } } #[test] @@ -160,112 +169,119 @@ fn should_fail_if_complainer_missing_in_transcript() { let rng = &mut reproducible_rng(); - let receivers_missing_complainer_id = IDkgReceivers::new(node_set(&[NODE_1])).unwrap(); - assert!(!receivers_missing_complainer_id - .get() - .contains(&COMPLAINER_ID)); - - let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing_with_invalid_internal(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: receivers_missing_complainer_id, - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - let csp = MockAllCryptoServiceProvider::new(); - let registry = registry_with(mega_encryption_pk_record(COMPLAINER_ID, REG_V1, rng)); - - let result = verify_complaint( - &csp, - registry.as_ref(), - &transcript, - &complaint, - COMPLAINER_ID, - ); - - assert_matches!( - result, - Err(IDkgVerifyComplaintError::InvalidArgumentMissingComplainerInTranscript { complainer_id }) - if complainer_id == COMPLAINER_ID - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let receivers_missing_complainer_id = IDkgReceivers::new(node_set(&[NODE_1])).unwrap(); + assert!(!receivers_missing_complainer_id + .get() + .contains(&COMPLAINER_ID)); + + let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing_with_invalid_internal(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: receivers_missing_complainer_id, + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + let csp = MockAllCryptoServiceProvider::new(); + let registry = registry_with(mega_encryption_pk_record(COMPLAINER_ID, REG_V1, rng)); + + let result = verify_complaint( + &csp, + registry.as_ref(), + &transcript, + &complaint, + COMPLAINER_ID, + ); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::InvalidArgumentMissingComplainerInTranscript { complainer_id }) + if complainer_id == COMPLAINER_ID + ); + } } #[test] fn should_fail_if_deserializing_complaint_fails() { - let invalid_internal_complaint_raw = b"invalid complaint".to_vec(); - let rng = &mut reproducible_rng(); - let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: invalid_internal_complaint_raw, - }; - let csp = MockAllCryptoServiceProvider::new(); - let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); - - let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); - - assert_matches!( - result, - Err(IDkgVerifyComplaintError::SerializationError { internal_error }) - if internal_error.contains("failed to deserialize complaint") - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let invalid_internal_complaint_raw = b"invalid complaint".to_vec(); + + let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: invalid_internal_complaint_raw, + }; + let csp = MockAllCryptoServiceProvider::new(); + let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); + + let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::SerializationError { internal_error }) + if internal_error.contains("failed to deserialize complaint") + ); + } } #[test] fn should_fail_if_deserializing_dealing_fails() { let rng = &mut reproducible_rng(); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing_with_invalid_internal(NODE_1)); - - let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - let csp = MockAllCryptoServiceProvider::new(); - let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); - let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); - - assert_matches!( - result, - Err(IDkgVerifyComplaintError::SerializationError { internal_error }) - if internal_error.contains("Error deserializing a signed dealing") - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing_with_invalid_internal(NODE_1)); + + let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + let csp = MockAllCryptoServiceProvider::new(); + let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); + + let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::SerializationError { internal_error }) + if internal_error.contains("Error deserializing a signed dealing") + ); + } } #[test] @@ -273,36 +289,39 @@ fn should_fail_if_complainer_mega_pubkey_not_in_registry() { let registry_missing_complainer_pubkey = registry_returning_none(); let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - - let result = verify_complaint( - &MockAllCryptoServiceProvider::new(), - registry_missing_complainer_pubkey.as_ref(), - &transcript, - &complaint, - NODE_1, - ); - assert_matches!( - result, - Err(IDkgVerifyComplaintError::ComplainerPublicKeyNotInRegistry { node_id, registry_version }) - if node_id == NODE_1 && registry_version == REG_V1 - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + + let result = verify_complaint( + &MockAllCryptoServiceProvider::new(), + registry_missing_complainer_pubkey.as_ref(), + &transcript, + &complaint, + NODE_1, + ); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::ComplainerPublicKeyNotInRegistry { node_id, registry_version }) + if node_id == NODE_1 && registry_version == REG_V1 + ); + } } #[test] @@ -311,36 +330,39 @@ fn should_fail_if_complainer_mega_pubkey_is_malformed() { registry_with(malformed_mega_encryption_pk_record(NODE_1, REG_V1)); let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - - let result = verify_complaint( - &MockAllCryptoServiceProvider::new(), - registry_with_malformed_complainer_pubkey.as_ref(), - &transcript, - &complaint, - NODE_1, - ); - assert_matches!( - result, - Err(IDkgVerifyComplaintError::MalformedComplainerPublicKey { node_id, .. }) - if node_id == NODE_1 - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + + let result = verify_complaint( + &MockAllCryptoServiceProvider::new(), + registry_with_malformed_complainer_pubkey.as_ref(), + &transcript, + &complaint, + NODE_1, + ); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::MalformedComplainerPublicKey { node_id, .. }) + if node_id == NODE_1 + ); + } } #[test] @@ -350,35 +372,38 @@ fn should_fail_if_complainer_mega_pubkey_algorithm_is_unsupported() { ); let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - let result = verify_complaint( - &MockAllCryptoServiceProvider::new(), - registry_with_unsupported_complainer_pubkey_algorithm.as_ref(), - &transcript, - &complaint, - NODE_1, - ); - - assert_matches!( - result, - Err(IDkgVerifyComplaintError::UnsupportedComplainerPublicKeyAlgorithm { .. }) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + + let result = verify_complaint( + &MockAllCryptoServiceProvider::new(), + registry_with_unsupported_complainer_pubkey_algorithm.as_ref(), + &transcript, + &complaint, + NODE_1, + ); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::UnsupportedComplainerPublicKeyAlgorithm { .. }) + ); + } } #[test] @@ -387,93 +412,102 @@ fn should_fail_if_registry_client_returns_error() { let registry_returning_error = registry_returning(registry_error.clone()); let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - - let result = verify_complaint( - &MockAllCryptoServiceProvider::new(), - registry_returning_error.as_ref(), - &transcript, - &complaint, - NODE_1, - ); - assert_matches!( - result, - Err(IDkgVerifyComplaintError::Registry(e)) if e == registry_error - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + + let result = verify_complaint( + &MockAllCryptoServiceProvider::new(), + registry_returning_error.as_ref(), + &transcript, + &complaint, + NODE_1, + ); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::Registry(e)) if e == registry_error + ); + } } #[test] fn should_return_ok_if_csp_returns_ok() { let rng = &mut reproducible_rng(); let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - let csp = csp_with_verify_complaint_returning(Ok(())); - let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); - - let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); - assert!(result.is_ok()); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + let csp = csp_with_verify_complaint_returning(Ok(())); + let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); + + let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); + + assert!(result.is_ok()); + } } #[test] fn should_return_error_if_csp_returns_error() { let rng = &mut reproducible_rng(); let transcript_id = IDkgTranscriptId::new(SUBNET_42, 27, Height::new(12)); - let mut verified_dealings = BTreeMap::new(); - verified_dealings.insert(0, batch_signed_dealing(NODE_1)); - let transcript = IDkgTranscript { - transcript_id, - receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), - registry_version: REG_V1, - verified_dealings, - transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, - internal_transcript_raw: vec![], - }; - let complaint = IDkgComplaint { - transcript_id, - dealer_id: NODE_1, - internal_complaint_raw: valid_internal_complaint_raw(), - }; - - let csp_error = IDkgVerifyComplaintError::InvalidComplaint; - let csp = csp_with_verify_complaint_returning(Err(csp_error.clone())); - let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); - let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); - - assert_matches!(result, Err(e) if e == csp_error); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let mut verified_dealings = BTreeMap::new(); + verified_dealings.insert(0, batch_signed_dealing(NODE_1)); + let transcript = IDkgTranscript { + transcript_id, + receivers: IDkgReceivers::new(node_set(&[NODE_1])).unwrap(), + registry_version: REG_V1, + verified_dealings, + transcript_type: IDkgTranscriptType::Masked(IDkgMaskedTranscriptOrigin::Random), + algorithm_id: alg, + internal_transcript_raw: vec![], + }; + let complaint = IDkgComplaint { + transcript_id, + dealer_id: NODE_1, + internal_complaint_raw: valid_internal_complaint_raw(), + }; + + let csp_error = IDkgVerifyComplaintError::InvalidComplaint; + let csp = csp_with_verify_complaint_returning(Err(csp_error.clone())); + let registry = registry_with(mega_encryption_pk_record(NODE_1, REG_V1, rng)); + + let result = verify_complaint(&csp, registry.as_ref(), &transcript, &complaint, NODE_1); + + assert_matches!(result, Err(e) if e == csp_error); + } } fn batch_signed_dealing(dealer_id: NodeId) -> BatchSignedIDkgDealing { diff --git a/rs/crypto/src/sign/canister_threshold_sig/idkg/dealing/tests.rs b/rs/crypto/src/sign/canister_threshold_sig/idkg/dealing/tests.rs index 576c5a1c94f..0b17619df46 100644 --- a/rs/crypto/src/sign/canister_threshold_sig/idkg/dealing/tests.rs +++ b/rs/crypto/src/sign/canister_threshold_sig/idkg/dealing/tests.rs @@ -26,90 +26,109 @@ mod verify_dealing_public { use ic_types::crypto::canister_threshold_sig::idkg::IDkgTranscriptOperation; use ic_types::crypto::canister_threshold_sig::idkg::InitialIDkgDealings; use ic_types::crypto::KeyPurpose::NodeSigning; - use ic_types::crypto::{CryptoError, Signable}; + use ic_types::crypto::{AlgorithmId, CryptoError, Signable}; use ic_types::registry::RegistryClientError; + use rand::Rng; #[test] fn should_fail_on_reproducible_registry_error() { let rng = &mut reproducible_rng(); - let registry_client_error = RegistryClientError::DecodeError { - error: "decode error".to_string(), - }; - let setup = - Setup::new_with_registry_client_get_value_error(registry_client_error.clone(), rng); - - assert_matches!( - verify_dealing_public( - &setup.csp, - &setup.registry_client, - setup.idkg_dealings.params(), - setup.idkg_dealings - .dealings() - .first() - .expect("should contain a dealing"), - ), - Err(IDkgVerifyDealingPublicError::InvalidSignature { - error, - crypto_error - }) - if error.contains("Invalid basic signature on signed iDKG dealing") - && crypto_error == CryptoError::RegistryClient(registry_client_error) - && crypto_error.is_reproducible() - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let registry_client_error = RegistryClientError::DecodeError { + error: "decode error".to_string(), + }; + + let setup = Setup::new_with_registry_client_get_value_error( + alg, + registry_client_error.clone(), + rng, + ); + + assert_matches!( + verify_dealing_public( + &setup.csp, + &setup.registry_client, + setup.idkg_dealings.params(), + setup.idkg_dealings + .dealings() + .first() + .expect("should contain a dealing"), + ), + Err(IDkgVerifyDealingPublicError::InvalidSignature { + error, + crypto_error + }) + if error.contains("Invalid basic signature on signed iDKG dealing") + && crypto_error == CryptoError::RegistryClient(registry_client_error) + && crypto_error.is_reproducible() + ); + } } #[test] fn should_fail_on_not_necessarily_reproducible_registry_error() { let rng = &mut reproducible_rng(); - let registry_client_error = RegistryClientError::VersionNotAvailable { - version: RegistryVersion::from(42), - }; - let setup = - Setup::new_with_registry_client_get_value_error(registry_client_error.clone(), rng); - - assert_matches!( - verify_dealing_public( - &setup.csp, - &setup.registry_client, - setup.idkg_dealings.params(), - setup.idkg_dealings - .dealings() - .first() - .expect("should contain a dealing"), - ), - Err(IDkgVerifyDealingPublicError::InvalidSignature { - error, - crypto_error - }) - if error.contains("Invalid basic signature on signed iDKG dealing") - && crypto_error == CryptoError::RegistryClient(registry_client_error) - && !crypto_error.is_reproducible() - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let registry_client_error = RegistryClientError::VersionNotAvailable { + version: RegistryVersion::from(rng.gen::() as u64), + }; + + let setup = Setup::new_with_registry_client_get_value_error( + alg, + registry_client_error.clone(), + rng, + ); + + assert_matches!( + verify_dealing_public( + &setup.csp, + &setup.registry_client, + setup.idkg_dealings.params(), + setup.idkg_dealings + .dealings() + .first() + .expect("should contain a dealing"), + ), + Err(IDkgVerifyDealingPublicError::InvalidSignature { + error, + crypto_error + }) + if error.contains("Invalid basic signature on signed iDKG dealing") + && crypto_error == CryptoError::RegistryClient(registry_client_error) + && !crypto_error.is_reproducible() + ); + } } #[test] fn should_fail_if_deserializing_operation_fails() { let rng = &mut reproducible_rng(); - let setup = Setup::new_with_dealer(node_id(37), valid_node_signing_public_key(), rng); - - // The deserialization of the operation fails if `internal_transcript_raw` is empty - assert_matches!( - setup.idkg_dealings.params().operation_type(), - IDkgTranscriptOperation::ReshareOfUnmasked(idkg_transcript) - if idkg_transcript.internal_transcript_raw.is_empty() - ); - - // The error returned if the deserialization of the operation fails is `InvalidDealing` - assert_matches!( - verify_dealing_public( - &setup.csp, - &setup.registry_client, - setup.idkg_dealings.params(), - &setup.signed_dealing.expect("should have a signed dealing") - ), - Err(IDkgVerifyDealingPublicError::InvalidDealing { reason }) - if reason.contains("EOF while parsing a value") - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let setup = + Setup::new_with_dealer(alg, node_id(37), valid_node_signing_public_key(), rng); + + // The deserialization of the operation fails if `internal_transcript_raw` is empty + assert_matches!( + setup.idkg_dealings.params().operation_type(), + IDkgTranscriptOperation::ReshareOfUnmasked(idkg_transcript) + if idkg_transcript.internal_transcript_raw.is_empty() + ); + + // The error returned if the deserialization of the operation fails is `InvalidDealing` + assert_matches!( + verify_dealing_public( + &setup.csp, + &setup.registry_client, + setup.idkg_dealings.params(), + &setup.signed_dealing.expect("should have a signed dealing") + ), + Err(IDkgVerifyDealingPublicError::InvalidDealing { reason }) + if reason.contains("EOF while parsing a value") + ); + } } struct Setup { @@ -121,6 +140,7 @@ mod verify_dealing_public { impl Setup { fn new_with_registry_client_get_value_error( + alg: AlgorithmId, registry_client_error: RegistryClientError, rng: &mut ReproducibleRng, ) -> Self { @@ -132,18 +152,19 @@ mod verify_dealing_public { csp.expect_verify().never(); Setup { registry_client, - idkg_dealings: dummy_initial_idkg_dealing_for_tests(rng), + idkg_dealings: dummy_initial_idkg_dealing_for_tests(alg, rng), csp, signed_dealing: None, } } fn new_with_dealer( + alg: AlgorithmId, dealer_id: NodeId, dealer_node_signing_public_key_proto: PublicKey, rng: &mut ReproducibleRng, ) -> Self { - let idkg_dealings = dummy_initial_idkg_dealing_for_tests(rng); + let idkg_dealings = dummy_initial_idkg_dealing_for_tests(alg, rng); let mut node_signing_public_key_bytes = Vec::new(); dealer_node_signing_public_key_proto .encode(&mut node_signing_public_key_bytes) diff --git a/rs/crypto/test_utils/canister_threshold_sigs/src/dummy_values.rs b/rs/crypto/test_utils/canister_threshold_sigs/src/dummy_values.rs index 7a59f3056a4..a370a05a84d 100644 --- a/rs/crypto/test_utils/canister_threshold_sigs/src/dummy_values.rs +++ b/rs/crypto/test_utils/canister_threshold_sigs/src/dummy_values.rs @@ -6,7 +6,7 @@ use ic_types::crypto::canister_threshold_sig::idkg::{ IDkgComplaint, IDkgDealing, IDkgOpening, IDkgTranscriptId, IDkgTranscriptOperation, IDkgTranscriptParams, InitialIDkgDealings, SignedIDkgDealing, }; -use ic_types::crypto::{BasicSig, BasicSigOf}; +use ic_types::crypto::{AlgorithmId, BasicSig, BasicSigOf}; use ic_types::signature::BasicSignature; use ic_types::{Height, NodeId, PrincipalId, SubnetId}; use rand::{CryptoRng, Rng}; @@ -52,10 +52,12 @@ pub fn dummy_dealings( } pub fn dummy_initial_idkg_dealing_for_tests( + alg: AlgorithmId, rng: &mut R, ) -> InitialIDkgDealings { let previous_receivers = set_of_nodes(&[35, 36, 37, 38]); let previous_transcript = mock_transcript( + alg, Some(previous_receivers), mock_unmasked_transcript_type(rng), rng, @@ -69,6 +71,7 @@ pub fn dummy_initial_idkg_dealing_for_tests( let receivers = set_of_nodes(&[39, 40, 41]); let previous_params = create_idkg_params( + alg, &dealers, &dealers, IDkgTranscriptOperation::ReshareOfUnmasked(previous_transcript), diff --git a/rs/crypto/test_utils/canister_threshold_sigs/src/lib.rs b/rs/crypto/test_utils/canister_threshold_sigs/src/lib.rs index 72ee1c374ba..72371da485e 100644 --- a/rs/crypto/test_utils/canister_threshold_sigs/src/lib.rs +++ b/rs/crypto/test_utils/canister_threshold_sigs/src/lib.rs @@ -33,6 +33,7 @@ use std::sync::Arc; pub mod dummy_values; pub fn create_idkg_params( + alg: AlgorithmId, dealer_set: &BTreeSet, receiver_set: &BTreeSet, operation: IDkgTranscriptOperation, @@ -43,7 +44,7 @@ pub fn create_idkg_params( dealer_set.clone(), receiver_set.clone(), RegistryVersion::from(0), - AlgorithmId::ThresholdEcdsaSecp256k1, + alg, operation, ) .expect("Should be able to create IDKG params") @@ -60,6 +61,7 @@ pub fn mock_masked_transcript_type() -> IDkgTranscriptType { } pub fn mock_transcript( + alg: AlgorithmId, receivers: Option>, transcript_type: IDkgTranscriptType, rng: &mut R, @@ -81,7 +83,7 @@ pub fn mock_transcript( registry_version: RegistryVersion::from(314), verified_dealings: BTreeMap::new(), transcript_type, - algorithm_id: AlgorithmId::ThresholdEcdsaSecp256k1, + algorithm_id: alg, internal_transcript_raw: vec![], } } diff --git a/rs/crypto/tests/canister_threshold_sigs.rs b/rs/crypto/tests/canister_threshold_sigs.rs index 07b5b7fe420..3d29cb31e21 100644 --- a/rs/crypto/tests/canister_threshold_sigs.rs +++ b/rs/crypto/tests/canister_threshold_sigs.rs @@ -44,268 +44,256 @@ mod create_dealing { #[test] fn should_create_signed_dealing_with_correct_public_key() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let dealer = env.nodes.random_dealer(¶ms, rng); - - let dealing = dealer - .create_dealing(¶ms) - .expect("could not create dealing"); - assert_eq!(dealing.dealer_id(), dealer.id()); - - let verification_result = dealer.verify_basic_sig( - &dealing.signature.signature, - &dealing.content, - dealer.id(), - params.registry_version(), - ); - assert_eq!(verification_result, Ok(())); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = + env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let dealer = env.nodes.random_dealer(¶ms, rng); + + let dealing = dealer + .create_dealing(¶ms) + .expect("could not create dealing"); + assert_eq!(dealing.dealer_id(), dealer.id()); + + let verification_result = dealer.verify_basic_sig( + &dealing.signature.signature, + &dealing.content, + dealer.id(), + params.registry_version(), + ); + assert_eq!(verification_result, Ok(())); + } } #[test] fn should_fail_create_dealing_if_registry_missing_mega_pubkey() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let mut env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - - let new_node_id = random_node_id_excluding(&env.nodes.ids(), rng); - let crypto_not_in_registry = Node::new(new_node_id, Arc::clone(&env.registry), rng); - env.nodes.insert(crypto_not_in_registry); - let (dealers, receivers_with_new_node_id) = { - let (random_dealers, random_receivers) = - env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let mut receivers_ids = random_receivers.get().clone(); - receivers_ids.insert(new_node_id); - let receivers_with_new_node_id = - IDkgReceivers::new(receivers_ids).expect("valid receivers"); - (random_dealers, receivers_with_new_node_id) - }; - let params = env.params_for_random_sharing( - &dealers, - &receivers_with_new_node_id, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let dealer = env.nodes.random_dealer(¶ms, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let mut env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + + let new_node_id = random_node_id_excluding(&env.nodes.ids(), rng); + let crypto_not_in_registry = Node::new(new_node_id, Arc::clone(&env.registry), rng); + env.nodes.insert(crypto_not_in_registry); + let (dealers, receivers_with_new_node_id) = { + let (random_dealers, random_receivers) = + env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); + let mut receivers_ids = random_receivers.get().clone(); + receivers_ids.insert(new_node_id); + let receivers_with_new_node_id = + IDkgReceivers::new(receivers_ids).expect("valid receivers"); + (random_dealers, receivers_with_new_node_id) + }; + let params = + env.params_for_random_sharing(&dealers, &receivers_with_new_node_id, alg, rng); + let dealer = env.nodes.random_dealer(¶ms, rng); - let result = dealer.create_dealing(¶ms); - assert_matches!(result, Err(IDkgCreateDealingError::PublicKeyNotFound { node_id, .. }) if node_id==new_node_id); + let result = dealer.create_dealing(¶ms); + assert_matches!(result, Err(IDkgCreateDealingError::PublicKeyNotFound { node_id, .. }) if node_id==new_node_id); + } } #[test] fn should_fail_create_dealing_if_node_isnt_a_dealer() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let bad_dealer_id = random_node_id_excluding(params.dealers().get(), rng); - let bad_dealer = Node::new(bad_dealer_id, Arc::clone(&env.registry), rng); - let result = bad_dealer.create_dealing(¶ms); - let err = result.unwrap_err(); - assert_matches!(err, IDkgCreateDealingError::NotADealer { node_id } if node_id==bad_dealer_id); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = + env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let bad_dealer_id = random_node_id_excluding(params.dealers().get(), rng); + let bad_dealer = Node::new(bad_dealer_id, Arc::clone(&env.registry), rng); + + let result = bad_dealer.create_dealing(¶ms); + let err = result.unwrap_err(); + assert_matches!(err, IDkgCreateDealingError::NotADealer { node_id } if node_id==bad_dealer_id); + } } #[test] fn should_fail_create_reshare_dealing_if_transcript_isnt_loaded() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = - env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let initial_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let initial_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&initial_params, rng); + let (dealers, receivers) = env + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let reshare_params = build_params_from_previous( - initial_params, - IDkgTranscriptOperation::ReshareOfMasked(initial_transcript.clone()), - rng, - ); - let dealer = env.nodes.random_dealer(&reshare_params, rng); + let initial_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let initial_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&initial_params, rng); + + let reshare_params = build_params_from_previous( + initial_params, + IDkgTranscriptOperation::ReshareOfMasked(initial_transcript.clone()), + rng, + ); + let dealer = env.nodes.random_dealer(&reshare_params, rng); - // We don't call `load_transcript`... + // We don't call `load_transcript`... - let result = dealer.create_dealing(&reshare_params); - let err = result.unwrap_err(); - assert_matches!(err, IDkgCreateDealingError::SecretSharesNotFound { .. }); + let result = dealer.create_dealing(&reshare_params); + let err = result.unwrap_err(); + assert_matches!(err, IDkgCreateDealingError::SecretSharesNotFound { .. }); - // Now, load the transcript and make sure it succeeds - dealer.load_transcript_or_panic(&initial_transcript); - let result = dealer.create_dealing(&reshare_params); - assert_matches!(result, Ok(_)); + // Now, load the transcript and make sure it succeeds + dealer.load_transcript_or_panic(&initial_transcript); + let result = dealer.create_dealing(&reshare_params); + assert_matches!(result, Ok(_)); + } } #[test] fn should_fail_to_create_dealing_when_kappa_unmasked_not_retained() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = - env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = env + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let masked_key_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + let masked_key_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let masked_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); + let masked_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); - let unmasked_key_params = build_params_from_previous( - masked_key_params, - IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript.clone()), - rng, - ); + let unmasked_key_params = build_params_from_previous( + masked_key_params, + IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript.clone()), + rng, + ); - let unmasked_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&unmasked_key_params, rng); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &unmasked_key_transcript, - rng, - ); + let unmasked_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&unmasked_key_params, rng); + let quadruple = generate_presig_quadruple( + &env, + &dealers, + &receivers, + alg, + &unmasked_key_transcript, + rng, + ); - let inputs = { - let derivation_path = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], - }; + let inputs = { + let derivation_path = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }; - let hashed_message = rng.gen::<[u8; 32]>(); - let seed = Randomness::from(rng.gen::<[u8; 32]>()); + let hashed_message = rng.gen::<[u8; 32]>(); + let seed = Randomness::from(rng.gen::<[u8; 32]>()); - ThresholdEcdsaSigInputs::new( - &derivation_path, - &hashed_message, - seed, - quadruple, - unmasked_key_transcript.clone(), - ) - .expect("failed to create signature inputs") - }; + ThresholdEcdsaSigInputs::new( + &derivation_path, + &hashed_message, + seed, + quadruple, + unmasked_key_transcript.clone(), + ) + .expect("failed to create signature inputs") + }; - let reshare_params = build_params_from_previous( - unmasked_key_params, - IDkgTranscriptOperation::ReshareOfUnmasked( - inputs.presig_quadruple().kappa_unmasked().clone(), - ), - rng, - ); - let dealer = env.nodes.random_dealer(&reshare_params, rng); - dealer.load_input_transcripts(&inputs); - - // make sure creating dealings succeeds with all the transcripts - let result = dealer.create_dealing(&reshare_params); - assert_matches!(result, Ok(_)); - - // Do not include kappa unmasked in retained transcripts - let active_transcripts = hashset!( - masked_key_transcript, - unmasked_key_transcript, - inputs.presig_quadruple().lambda_masked().clone(), - inputs.presig_quadruple().kappa_times_lambda().clone(), - inputs.presig_quadruple().key_times_lambda().clone(), - ); - assert_eq!( - dealer.retain_active_transcripts(&active_transcripts), - Ok(()) - ); + let reshare_params = build_params_from_previous( + unmasked_key_params, + IDkgTranscriptOperation::ReshareOfUnmasked( + inputs.presig_quadruple().kappa_unmasked().clone(), + ), + rng, + ); + let dealer = env.nodes.random_dealer(&reshare_params, rng); + dealer.load_input_transcripts(&inputs); + + // make sure creating dealings succeeds with all the transcripts + let result = dealer.create_dealing(&reshare_params); + assert_matches!(result, Ok(_)); + + // Do not include kappa unmasked in retained transcripts + let active_transcripts = hashset!( + masked_key_transcript, + unmasked_key_transcript, + inputs.presig_quadruple().lambda_masked().clone(), + inputs.presig_quadruple().kappa_times_lambda().clone(), + inputs.presig_quadruple().key_times_lambda().clone(), + ); + assert_eq!( + dealer.retain_active_transcripts(&active_transcripts), + Ok(()) + ); - // Create dealing should now fail - let result = dealer.create_dealing(&reshare_params); - assert_matches!( - result, - Err(IDkgCreateDealingError::SecretSharesNotFound { .. }) - ); + // Create dealing should now fail + let result = dealer.create_dealing(&reshare_params); + assert_matches!( + result, + Err(IDkgCreateDealingError::SecretSharesNotFound { .. }) + ); + } } #[test] fn should_fail_to_create_dealing_when_reshared_unmasked_key_transcript_not_retained() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = - env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = env + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let masked_key_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + let masked_key_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let masked_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); + let masked_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); - let unmasked_key_params = build_params_from_previous( - masked_key_params, - IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript.clone()), - rng, - ); + let unmasked_key_params = build_params_from_previous( + masked_key_params, + IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript.clone()), + rng, + ); - let unmasked_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&unmasked_key_params, rng); + let unmasked_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&unmasked_key_params, rng); - let reshare_params = build_params_from_previous( - unmasked_key_params, - IDkgTranscriptOperation::ReshareOfUnmasked(unmasked_key_transcript.clone()), - rng, - ); + let reshare_params = build_params_from_previous( + unmasked_key_params, + IDkgTranscriptOperation::ReshareOfUnmasked(unmasked_key_transcript.clone()), + rng, + ); - let dealer = env.nodes.random_dealer(&reshare_params, rng); - dealer.load_transcript_or_panic(&masked_key_transcript); - dealer.load_transcript_or_panic(&unmasked_key_transcript); + let dealer = env.nodes.random_dealer(&reshare_params, rng); + dealer.load_transcript_or_panic(&masked_key_transcript); + dealer.load_transcript_or_panic(&unmasked_key_transcript); - // make sure creating dealings succeeds with all the transcripts - let result = dealer.create_dealing(&reshare_params); - assert_matches!(result, Ok(_)); + // make sure creating dealings succeeds with all the transcripts + let result = dealer.create_dealing(&reshare_params); + assert_matches!(result, Ok(_)); - // Do not include shared unmasked key transcript in retained transcripts - let active_transcripts = hashset!(masked_key_transcript,); - assert_eq!( - dealer.retain_active_transcripts(&active_transcripts), - Ok(()) - ); + // Do not include shared unmasked key transcript in retained transcripts + let active_transcripts = hashset!(masked_key_transcript,); + assert_eq!( + dealer.retain_active_transcripts(&active_transcripts), + Ok(()) + ); - // Create dealing should now fail - let result = dealer.create_dealing(&reshare_params); - assert_matches!( - result, - Err(IDkgCreateDealingError::SecretSharesNotFound { .. }) - ); + // Create dealing should now fail + let result = dealer.create_dealing(&reshare_params); + assert_matches!( + result, + Err(IDkgCreateDealingError::SecretSharesNotFound { .. }) + ); + } } } @@ -316,208 +304,201 @@ mod create_transcript { #[test] fn should_create_transcript() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); - let batch_signed_dealings = env - .nodes - .support_dealings_from_all_receivers(signed_dealings, ¶ms); - let creator = env.nodes.random_receiver(params.receivers(), rng); - let result = creator.create_transcript(¶ms, &batch_signed_dealings); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = + env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); + let batch_signed_dealings = env + .nodes + .support_dealings_from_all_receivers(signed_dealings, ¶ms); + + let creator = env.nodes.random_receiver(params.receivers(), rng); + let result = creator.create_transcript(¶ms, &batch_signed_dealings); - assert_matches!(result, Ok(transcript) if transcript.transcript_id == params.transcript_id()) + assert_matches!(result, Ok(transcript) if transcript.transcript_id == params.transcript_id()) + } } #[test] fn should_fail_create_transcript_without_enough_dealings() { let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(1..30); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..30); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let dealings: BTreeMap = env - .nodes - .dealers(¶ms) - .take(params.collection_threshold().get() as usize - 1) // NOTE: Not enough! - .map(|dealer| { - let dealing = env.nodes.create_and_verify_signed_dealing(¶ms, dealer); - (dealer.id(), dealing) - }) - .collect(); + let (dealers, receivers) = + env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let batch_signed_dealings = env - .nodes - .support_dealings_from_all_receivers(dealings.clone(), ¶ms); - let creator = env.nodes.random_receiver(params.receivers(), rng); + let dealings: BTreeMap = env + .nodes + .dealers(¶ms) + .take(params.collection_threshold().get() as usize - 1) // NOTE: Not enough! + .map(|dealer| { + let dealing = env.nodes.create_and_verify_signed_dealing(¶ms, dealer); + (dealer.id(), dealing) + }) + .collect(); + + let batch_signed_dealings = env + .nodes + .support_dealings_from_all_receivers(dealings.clone(), ¶ms); + let creator = env.nodes.random_receiver(params.receivers(), rng); - let result = creator.create_transcript(¶ms, &batch_signed_dealings); + let result = creator.create_transcript(¶ms, &batch_signed_dealings); - let err = result.unwrap_err(); - assert_matches!( - err, - IDkgCreateTranscriptError::UnsatisfiedCollectionThreshold { threshold, dealing_count } - if (threshold as usize)==(params.collection_threshold().get() as usize) && dealing_count==dealings.len() - ); + let err = result.unwrap_err(); + assert_matches!( + err, + IDkgCreateTranscriptError::UnsatisfiedCollectionThreshold { threshold, dealing_count } + if (threshold as usize)==(params.collection_threshold().get() as usize) && dealing_count==dealings.len() + ); + } } #[test] fn should_fail_create_transcript_with_disallowed_dealer() { const MIN_NUM_NODES: usize = 2; let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(MIN_NUM_NODES..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers( - &IDkgParticipants::RandomWithAtLeast { - min_num_dealers: MIN_NUM_NODES, - min_num_receivers: 1, - }, - rng, - ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); - let batch_signed_dealings = env - .nodes - .support_dealings_from_all_receivers(signed_dealings, ¶ms); - - let params_with_removed_dealer = { - let mut dealers = params.dealers().get().clone(); - let removed_dealer_id = random_dealer_id(¶ms, rng); - assert!(dealers.remove(&removed_dealer_id)); - IDkgTranscriptParams::new( - params.transcript_id(), - dealers, - params.receivers().get().clone(), - params.registry_version(), - params.algorithm_id(), - params.operation_type().clone(), - ) - .expect("valid IDkgTranscriptParams") - }; - let creator = env.nodes.random_receiver(params.receivers(), rng); - let result = creator.create_transcript(¶ms_with_removed_dealer, &batch_signed_dealings); - assert_matches!( - result, - Err(IDkgCreateTranscriptError::DealerNotAllowed { .. }) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(MIN_NUM_NODES..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = env.choose_dealers_and_receivers( + &IDkgParticipants::RandomWithAtLeast { + min_num_dealers: MIN_NUM_NODES, + min_num_receivers: 1, + }, + rng, + ); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); + let batch_signed_dealings = env + .nodes + .support_dealings_from_all_receivers(signed_dealings, ¶ms); + + let params_with_removed_dealer = { + let mut dealers = params.dealers().get().clone(); + let removed_dealer_id = random_dealer_id(¶ms, rng); + assert!(dealers.remove(&removed_dealer_id)); + IDkgTranscriptParams::new( + params.transcript_id(), + dealers, + params.receivers().get().clone(), + params.registry_version(), + params.algorithm_id(), + params.operation_type().clone(), + ) + .expect("valid IDkgTranscriptParams") + }; + let creator = env.nodes.random_receiver(params.receivers(), rng); + let result = + creator.create_transcript(¶ms_with_removed_dealer, &batch_signed_dealings); + + assert_matches!( + result, + Err(IDkgCreateTranscriptError::DealerNotAllowed { .. }) + ); + } } #[test] fn should_fail_create_transcript_with_signature_by_disallowed_receiver() { const MIN_NUM_NODES: usize = 2; // Need enough to be able to remove one let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(MIN_NUM_NODES..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers( - &IDkgParticipants::RandomWithAtLeast { - min_num_dealers: 1, - min_num_receivers: MIN_NUM_NODES, - }, - rng, - ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(MIN_NUM_NODES..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = env.choose_dealers_and_receivers( + &IDkgParticipants::RandomWithAtLeast { + min_num_dealers: 1, + min_num_receivers: MIN_NUM_NODES, + }, + rng, + ); - let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); - let batch_signed_dealings = env - .nodes - .support_dealings_from_all_receivers(signed_dealings, ¶ms); - - // Remove one of the original receivers from the params - // so that we have a valid sig on the dealing, but `create_transcript` will not - // consider them eligible to sign - let (removed_receiver_id, modified_params) = { - let mut modified_receivers = params.receivers().get().clone(); - let removed_node_id = random_receiver_id(¶ms, rng); - assert!(modified_receivers.remove(&removed_node_id)); - let modified_params = IDkgTranscriptParams::new( - params.transcript_id(), - params.dealers().get().clone(), - modified_receivers, - params.registry_version(), - params.algorithm_id(), - params.operation_type().clone(), - ) - .expect("failed to create new params"); - (removed_node_id, modified_params) - }; + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let creator = env.nodes.random_receiver(modified_params.receivers(), rng); - let result = creator.create_transcript(&modified_params, &batch_signed_dealings); - let err = result.unwrap_err(); - assert_matches!( - err, - IDkgCreateTranscriptError::SignerNotAllowed { - node_id - } - if node_id == removed_receiver_id - ); + let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); + let batch_signed_dealings = env + .nodes + .support_dealings_from_all_receivers(signed_dealings, ¶ms); + + // Remove one of the original receivers from the params + // so that we have a valid sig on the dealing, but `create_transcript` will not + // consider them eligible to sign + let (removed_receiver_id, modified_params) = { + let mut modified_receivers = params.receivers().get().clone(); + let removed_node_id = random_receiver_id(¶ms, rng); + assert!(modified_receivers.remove(&removed_node_id)); + let modified_params = IDkgTranscriptParams::new( + params.transcript_id(), + params.dealers().get().clone(), + modified_receivers, + params.registry_version(), + params.algorithm_id(), + params.operation_type().clone(), + ) + .expect("failed to create new params"); + (removed_node_id, modified_params) + }; + + let creator = env.nodes.random_receiver(modified_params.receivers(), rng); + let result = creator.create_transcript(&modified_params, &batch_signed_dealings); + let err = result.unwrap_err(); + assert_matches!( + err, + IDkgCreateTranscriptError::SignerNotAllowed { + node_id + } + if node_id == removed_receiver_id + ); + } } #[test] fn should_fail_create_transcript_without_enough_signatures() { const MIN_NUM_NODES: usize = 4; // Needs to be enough for >=1 signature let rng = &mut reproducible_rng(); - let subnet_size = rng.gen_range(MIN_NUM_NODES..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = env.choose_dealers_and_receivers( - &IDkgParticipants::RandomWithAtLeast { - min_num_dealers: 1, - min_num_receivers: MIN_NUM_NODES, - }, - rng, - ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(MIN_NUM_NODES..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = env.choose_dealers_and_receivers( + &IDkgParticipants::RandomWithAtLeast { + min_num_dealers: 1, + min_num_receivers: MIN_NUM_NODES, + }, + rng, + ); - let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); - let insufficient_supporters: Nodes = env - .nodes - .into_receivers(params.receivers()) - .take(params.verification_threshold().get() as usize - 1) // Not enough! - .collect(); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let insufficient_batch_signed_dealings = - insufficient_supporters.support_dealings_from_all_receivers(signed_dealings, ¶ms); + let signed_dealings = env.nodes.create_and_verify_signed_dealings(¶ms); + let insufficient_supporters: Nodes = env + .nodes + .into_receivers(params.receivers()) + .take(params.verification_threshold().get() as usize - 1) // Not enough! + .collect(); - let creator = insufficient_supporters.random_receiver(params.receivers(), rng); - let result = creator.create_transcript(¶ms, &insufficient_batch_signed_dealings); - let err = result.unwrap_err(); - assert_matches!( - err, - IDkgCreateTranscriptError::UnsatisfiedVerificationThreshold { threshold, signature_count, .. } - if threshold == params.verification_threshold().get() && signature_count == (threshold as usize - 1) - ); + let insufficient_batch_signed_dealings = insufficient_supporters + .support_dealings_from_all_receivers(signed_dealings, ¶ms); + + let creator = insufficient_supporters.random_receiver(params.receivers(), rng); + let result = creator.create_transcript(¶ms, &insufficient_batch_signed_dealings); + let err = result.unwrap_err(); + assert_matches!( + err, + IDkgCreateTranscriptError::UnsatisfiedVerificationThreshold { threshold, signature_count, .. } + if threshold == params.verification_threshold().get() && signature_count == (threshold as usize - 1) + ); + } } #[test] @@ -526,30 +507,28 @@ mod create_transcript { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let creator = env.nodes.random_receiver(params.receivers(), rng); - let batch_signed_dealings = env.nodes.create_batch_signed_dealings(¶ms); - let corrupted_dealings = batch_signed_dealings - .into_iter() - .map(|mut dealing| { - dealing.flip_a_bit_in_all(); - dealing - }) - .collect(); - let result = creator.create_transcript(¶ms, &corrupted_dealings); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let creator = env.nodes.random_receiver(params.receivers(), rng); + let batch_signed_dealings = env.nodes.create_batch_signed_dealings(¶ms); + let corrupted_dealings = batch_signed_dealings + .into_iter() + .map(|mut dealing| { + dealing.flip_a_bit_in_all(); + dealing + }) + .collect(); - assert_matches!( - result, - Err(IDkgCreateTranscriptError::InvalidSignatureBatch { - crypto_error: CryptoError::SignatureVerification { .. } - }) - ); + let result = creator.create_transcript(¶ms, &corrupted_dealings); + + assert_matches!( + result, + Err(IDkgCreateTranscriptError::InvalidSignatureBatch { + crypto_error: CryptoError::SignatureVerification { .. } + }) + ); + } } #[test] @@ -558,32 +537,30 @@ mod create_transcript { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let creator = env.nodes.random_receiver(params.receivers(), rng); - let mut batch_signed_dealings = env.nodes.create_batch_signed_dealings(¶ms); - batch_signed_dealings.insert_or_update({ - let mut corrupted_dealing = batch_signed_dealings - .iter() - .next() - .expect("at least one dealing to corrupt") - .clone(); - corrupted_dealing.flip_a_bit_in_all(); - corrupted_dealing - }); - let result = creator.create_transcript(¶ms, &batch_signed_dealings); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let creator = env.nodes.random_receiver(params.receivers(), rng); + let mut batch_signed_dealings = env.nodes.create_batch_signed_dealings(¶ms); + batch_signed_dealings.insert_or_update({ + let mut corrupted_dealing = batch_signed_dealings + .iter() + .next() + .expect("at least one dealing to corrupt") + .clone(); + corrupted_dealing.flip_a_bit_in_all(); + corrupted_dealing + }); + + let result = creator.create_transcript(¶ms, &batch_signed_dealings); - assert_matches!( - result, - Err(IDkgCreateTranscriptError::InvalidSignatureBatch { - crypto_error: CryptoError::SignatureVerification { .. } - }) - ); + assert_matches!( + result, + Err(IDkgCreateTranscriptError::InvalidSignatureBatch { + crypto_error: CryptoError::SignatureVerification { .. } + }) + ); + } } #[test] @@ -592,32 +569,30 @@ mod create_transcript { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let creator = env.nodes.random_receiver(params.receivers(), rng); - let mut batch_signed_dealings = env.nodes.create_batch_signed_dealings(¶ms); - batch_signed_dealings.insert_or_update({ - let mut corrupted_dealing = batch_signed_dealings - .iter() - .next() - .expect("at least one dealing to corrupt") - .clone(); - corrupted_dealing.flip_a_bit_in_one(); - corrupted_dealing - }); - let result = creator.create_transcript(¶ms, &batch_signed_dealings); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let creator = env.nodes.random_receiver(params.receivers(), rng); + let mut batch_signed_dealings = env.nodes.create_batch_signed_dealings(¶ms); + batch_signed_dealings.insert_or_update({ + let mut corrupted_dealing = batch_signed_dealings + .iter() + .next() + .expect("at least one dealing to corrupt") + .clone(); + corrupted_dealing.flip_a_bit_in_one(); + corrupted_dealing + }); + + let result = creator.create_transcript(¶ms, &batch_signed_dealings); - assert_matches!( - result, - Err(IDkgCreateTranscriptError::InvalidSignatureBatch { - crypto_error: CryptoError::SignatureVerification { .. } - }) - ); + assert_matches!( + result, + Err(IDkgCreateTranscriptError::InvalidSignatureBatch { + crypto_error: CryptoError::SignatureVerification { .. } + }) + ); + } } } @@ -632,26 +607,23 @@ mod load_transcript { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let not_participating_node_id = random_node_id_excluding(&env.nodes.ids(), rng); - let not_participating_node = - Node::new(not_participating_node_id, Arc::clone(&env.registry), rng); + let not_participating_node_id = random_node_id_excluding(&env.nodes.ids(), rng); + let not_participating_node = + Node::new(not_participating_node_id, Arc::clone(&env.registry), rng); - assert!(!transcript - .receivers - .get() - .contains(¬_participating_node_id)); - let result = not_participating_node.load_transcript(&transcript); - assert_matches!(result, Ok(_)); + assert!(!transcript + .receivers + .get() + .contains(¬_participating_node_id)); + let result = not_participating_node.load_transcript(&transcript); + assert_matches!(result, Ok(_)); + } } #[test] @@ -660,19 +632,17 @@ mod load_transcript { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let loader = env.nodes.random_receiver(params.receivers(), rng); - assert_matches!(loader.load_transcript(&transcript), Ok(_)); - assert_matches!(loader.load_transcript(&transcript), Ok(_)); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let loader = env.nodes.random_receiver(params.receivers(), rng); + + assert_matches!(loader.load_transcript(&transcript), Ok(_)); + assert_matches!(loader.load_transcript(&transcript), Ok(_)); + } } #[test] @@ -682,20 +652,17 @@ mod load_transcript { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let loader = env.nodes.random_receiver(params.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let loader = env.nodes.random_receiver(params.receivers(), rng); - let result = loader.load_transcript(&transcript); + let result = loader.load_transcript(&transcript); - assert_matches!(result, Ok(complaints) if complaints.is_empty()); + assert_matches!(result, Ok(complaints) if complaints.is_empty()); + } } } @@ -708,24 +675,22 @@ mod verify_complaint { let subnet_size = rng.gen_range(2..6); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let result = env - .nodes - .random_receiver(params.receivers(), rng) - .verify_complaint(&transcript, complainer.id(), &complaint); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + + let result = env + .nodes + .random_receiver(params.receivers(), rng) + .verify_complaint(&transcript, complainer.id(), &complaint); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] @@ -734,38 +699,36 @@ mod verify_complaint { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let num_of_complaints = rng.gen_range(1..=transcript.verified_dealings.len()); - let (complainer, corrupted_dealing_indices, complaints) = - generate_complaints(&mut transcript, num_of_complaints, ¶ms, &env, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - for complaint in &complaints { - assert_eq!(complaint.transcript_id, transcript.transcript_id); - assert_eq!( - env.nodes - .random_receiver(params.receivers(), rng) - .verify_complaint(&transcript, complainer.id(), complaint), - Ok(()) - ); - } - // Ensure the complaints' dealer IDs are correct - for index in corrupted_dealing_indices { - let dealer_id = transcript - .dealer_id_for_index(index) - .expect("cannot find dealer ID for index"); - let dealer_for_index_exists_in_complaints = complaints - .iter() - .any(|complaint| complaint.dealer_id == dealer_id); - assert!(dealer_for_index_exists_in_complaints); + let num_of_complaints = rng.gen_range(1..=transcript.verified_dealings.len()); + let (complainer, corrupted_dealing_indices, complaints) = + generate_complaints(&mut transcript, num_of_complaints, ¶ms, &env, rng); + + for complaint in &complaints { + assert_eq!(complaint.transcript_id, transcript.transcript_id); + assert_eq!( + env.nodes + .random_receiver(params.receivers(), rng) + .verify_complaint(&transcript, complainer.id(), complaint), + Ok(()) + ); + } + // Ensure the complaints' dealer IDs are correct + for index in corrupted_dealing_indices { + let dealer_id = transcript + .dealer_id_for_index(index) + .expect("cannot find dealer ID for index"); + let dealer_for_index_exists_in_complaints = complaints + .iter() + .any(|complaint| complaint.dealer_id == dealer_id); + assert!(dealer_for_index_exists_in_complaints); + } } } @@ -782,27 +745,25 @@ mod verify_complaint { }, rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let wrong_complainer_id = - random_receiver_id_excluding(params.receivers(), complainer.id(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); - assert_matches!( - env.nodes - .random_receiver(params.receivers(), rng) - .verify_complaint(&transcript, wrong_complainer_id, &complaint,), - Err(IDkgVerifyComplaintError::InvalidComplaint) - ); + let wrong_complainer_id = + random_receiver_id_excluding(params.receivers(), complainer.id(), rng); + + assert_matches!( + env.nodes + .random_receiver(params.receivers(), rng) + .verify_complaint(&transcript, wrong_complainer_id, &complaint,), + Err(IDkgVerifyComplaintError::InvalidComplaint) + ); + } } #[test] @@ -811,41 +772,34 @@ mod verify_complaint { let subnet_size = rng.gen_range(2..6); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let other_transcript_id = env - .params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ) - .transcript_id(); - assert_ne!(other_transcript_id, params.transcript_id()); - let complaint = complaint - .into_builder() - .with_transcript_id(other_transcript_id) - .build(); - - let result = env - .nodes - .random_receiver(params.receivers(), rng) - .verify_complaint(&transcript, complainer.id(), &complaint); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + + let other_transcript_id = env + .params_for_random_sharing(&dealers, &receivers, alg, rng) + .transcript_id(); + assert_ne!(other_transcript_id, params.transcript_id()); + let complaint = complaint + .into_builder() + .with_transcript_id(other_transcript_id) + .build(); - assert_matches!( - result, - Err(IDkgVerifyComplaintError::InvalidArgumentMismatchingTranscriptIDs) - ); + let result = env + .nodes + .random_receiver(params.receivers(), rng) + .verify_complaint(&transcript, complainer.id(), &complaint); + + assert_matches!( + result, + Err(IDkgVerifyComplaintError::InvalidArgumentMismatchingTranscriptIDs) + ); + } } #[test] @@ -868,39 +822,37 @@ mod verify_complaint { }, rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - assert!(params.collection_threshold().get() >= 2); - let num_of_dealings_to_corrupt = 2; - let (complainer, _, complaints) = generate_complaints( - &mut transcript, - num_of_dealings_to_corrupt, - ¶ms, - &env, - rng, - ); - let complainer_id = complainer.id(); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + assert!(params.collection_threshold().get() >= 2); + let num_of_dealings_to_corrupt = 2; + + let (complainer, _, complaints) = generate_complaints( + &mut transcript, + num_of_dealings_to_corrupt, + ¶ms, + &env, + rng, + ); + let complainer_id = complainer.id(); - let mut complaint_1 = complaints.get(0).unwrap().clone(); - let mut complaint_2 = complaints.get(1).unwrap().clone(); - std::mem::swap(&mut complaint_1.dealer_id, &mut complaint_2.dealer_id); + let mut complaint_1 = complaints.get(0).unwrap().clone(); + let mut complaint_2 = complaints.get(1).unwrap().clone(); + std::mem::swap(&mut complaint_1.dealer_id, &mut complaint_2.dealer_id); - assert_matches!( - complainer.verify_complaint(&transcript, complainer_id, &complaint_1,), - Err(IDkgVerifyComplaintError::InvalidComplaint) - ); - assert_matches!( - complainer.verify_complaint(&transcript, complainer_id, &complaint_2,), - Err(IDkgVerifyComplaintError::InvalidComplaint) - ); + assert_matches!( + complainer.verify_complaint(&transcript, complainer_id, &complaint_1,), + Err(IDkgVerifyComplaintError::InvalidComplaint) + ); + assert_matches!( + complainer.verify_complaint(&transcript, complainer_id, &complaint_2,), + Err(IDkgVerifyComplaintError::InvalidComplaint) + ); + } } #[test] @@ -924,41 +876,39 @@ mod verify_complaint { }, rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - assert!(params.collection_threshold().get() as usize >= num_of_dealings_to_corrupt); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let (complainer, _, complaints) = generate_complaints( - &mut transcript, - num_of_dealings_to_corrupt, - ¶ms, - &env, - rng, - ); - let complainer_id = complainer.id(); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + assert!(params.collection_threshold().get() as usize >= num_of_dealings_to_corrupt); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let mut complaint_1 = complaints.get(0).unwrap().clone(); - let mut complaint_2 = complaints.get(1).unwrap().clone(); - std::mem::swap( - &mut complaint_1.internal_complaint_raw, - &mut complaint_2.internal_complaint_raw, - ); + let (complainer, _, complaints) = generate_complaints( + &mut transcript, + num_of_dealings_to_corrupt, + ¶ms, + &env, + rng, + ); + let complainer_id = complainer.id(); - assert_matches!( - complainer.verify_complaint(&transcript, complainer_id, &complaint_1,), - Err(IDkgVerifyComplaintError::InvalidComplaint) - ); - assert_matches!( - complainer.verify_complaint(&transcript, complainer_id, &complaint_2,), - Err(IDkgVerifyComplaintError::InvalidComplaint) - ); + let mut complaint_1 = complaints.get(0).unwrap().clone(); + let mut complaint_2 = complaints.get(1).unwrap().clone(); + std::mem::swap( + &mut complaint_1.internal_complaint_raw, + &mut complaint_2.internal_complaint_raw, + ); + + assert_matches!( + complainer.verify_complaint(&transcript, complainer_id, &complaint_1,), + Err(IDkgVerifyComplaintError::InvalidComplaint) + ); + assert_matches!( + complainer.verify_complaint(&transcript, complainer_id, &complaint_2,), + Err(IDkgVerifyComplaintError::InvalidComplaint) + ); + } } } @@ -973,18 +923,16 @@ mod verify_transcript { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - // Transcript should have correct dealer indexes - check_dealer_indexes(¶ms, &transcript); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + + // Transcript should have correct dealer indexes + check_dealer_indexes(¶ms, &transcript); + } } #[test] @@ -995,30 +943,27 @@ mod verify_transcript { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let initial_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let initial_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&initial_params, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let initial_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let initial_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&initial_params, rng); - // Initial transcript should have correct dealer indexes - check_dealer_indexes(&initial_params, &initial_transcript); + // Initial transcript should have correct dealer indexes + check_dealer_indexes(&initial_params, &initial_transcript); - let reshare_params = build_params_from_previous( - initial_params, - IDkgTranscriptOperation::ReshareOfMasked(initial_transcript), - rng, - ); - let reshare_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let reshare_params = build_params_from_previous( + initial_params, + IDkgTranscriptOperation::ReshareOfMasked(initial_transcript), + rng, + ); + let reshare_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - // Reshare transcript should have correct dealer indexes - check_dealer_indexes(&reshare_params, &reshare_transcript); + // Reshare transcript should have correct dealer indexes + check_dealer_indexes(&reshare_params, &reshare_transcript); + } } #[test] @@ -1029,38 +974,35 @@ mod verify_transcript { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let initial_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let initial_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&initial_params, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let initial_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let initial_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&initial_params, rng); - // Initial transcript should have correct dealer indexes - check_dealer_indexes(&initial_params, &initial_transcript); + // Initial transcript should have correct dealer indexes + check_dealer_indexes(&initial_params, &initial_transcript); - let unmasked_params = build_params_from_previous( - initial_params, - IDkgTranscriptOperation::ReshareOfMasked(initial_transcript), - rng, - ); - let unmasked_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&unmasked_params, rng); + let unmasked_params = build_params_from_previous( + initial_params, + IDkgTranscriptOperation::ReshareOfMasked(initial_transcript), + rng, + ); + let unmasked_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&unmasked_params, rng); - let reshare_params = build_params_from_previous( - unmasked_params, - IDkgTranscriptOperation::ReshareOfUnmasked(unmasked_transcript), - rng, - ); - let reshare_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let reshare_params = build_params_from_previous( + unmasked_params, + IDkgTranscriptOperation::ReshareOfUnmasked(unmasked_transcript), + rng, + ); + let reshare_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - check_dealer_indexes(&reshare_params, &reshare_transcript); + check_dealer_indexes(&reshare_params, &reshare_transcript); + } } #[test] @@ -1071,56 +1013,52 @@ mod verify_transcript { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let masked_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let masked_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&masked_params, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let masked_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let masked_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&masked_params, rng); - // Masked transcript should have correct dealer indexes - check_dealer_indexes(&masked_params, &masked_transcript); + // Masked transcript should have correct dealer indexes + check_dealer_indexes(&masked_params, &masked_transcript); - let unmasked_transcript = { - let masked_random_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let masked_random_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&masked_random_params, rng); + let unmasked_transcript = { + let masked_random_params = + env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let masked_random_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&masked_random_params, rng); - let unmasked_params = build_params_from_previous( - masked_random_params, - IDkgTranscriptOperation::ReshareOfMasked(masked_random_transcript), - rng, - ); - let unmasked_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&unmasked_params, rng); + let unmasked_params = build_params_from_previous( + masked_random_params, + IDkgTranscriptOperation::ReshareOfMasked(masked_random_transcript), + rng, + ); + let unmasked_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&unmasked_params, rng); - // Unmasked transcript should have correct dealer indexes - check_dealer_indexes(&unmasked_params, &unmasked_transcript); + // Unmasked transcript should have correct dealer indexes + check_dealer_indexes(&unmasked_params, &unmasked_transcript); - unmasked_transcript - }; + unmasked_transcript + }; - let multiplication_params = build_params_from_previous( - masked_params, - IDkgTranscriptOperation::UnmaskedTimesMasked(unmasked_transcript, masked_transcript), - rng, - ); - let multiplication_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&multiplication_params, rng); + let multiplication_params = build_params_from_previous( + masked_params, + IDkgTranscriptOperation::UnmaskedTimesMasked( + unmasked_transcript, + masked_transcript, + ), + rng, + ); + let multiplication_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&multiplication_params, rng); - // Multiplication transcript should have correct dealer indexes - check_dealer_indexes(&multiplication_params, &multiplication_transcript); + // Multiplication transcript should have correct dealer indexes + check_dealer_indexes(&multiplication_params, &multiplication_transcript); + } } #[test] @@ -1131,62 +1069,62 @@ mod verify_transcript { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let random_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let random_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&random_params, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let random_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let random_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&random_params, rng); - assert_eq!( - random_transcript.verified_dealings.len(), - random_params.collection_threshold().get() as usize - ); + assert_eq!( + random_transcript.verified_dealings.len(), + random_params.collection_threshold().get() as usize + ); - let unmasked_params = build_params_from_previous( - random_params.clone(), - IDkgTranscriptOperation::ReshareOfMasked(random_transcript.clone()), - rng, - ); - let unmasked_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&unmasked_params, rng); + let unmasked_params = build_params_from_previous( + random_params.clone(), + IDkgTranscriptOperation::ReshareOfMasked(random_transcript.clone()), + rng, + ); + let unmasked_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&unmasked_params, rng); - assert_eq!( - unmasked_transcript.verified_dealings.len(), - unmasked_params.collection_threshold().get() as usize - ); + assert_eq!( + unmasked_transcript.verified_dealings.len(), + unmasked_params.collection_threshold().get() as usize + ); - let reshare_params = build_params_from_previous( - unmasked_params, - IDkgTranscriptOperation::ReshareOfUnmasked(unmasked_transcript.clone()), - rng, - ); - let reshare_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let reshare_params = build_params_from_previous( + unmasked_params, + IDkgTranscriptOperation::ReshareOfUnmasked(unmasked_transcript.clone()), + rng, + ); + let reshare_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - assert_eq!( - reshare_transcript.verified_dealings.len(), - reshare_params.collection_threshold().get() as usize - ); + assert_eq!( + reshare_transcript.verified_dealings.len(), + reshare_params.collection_threshold().get() as usize + ); - let multiplication_params = build_params_from_previous( - random_params, - IDkgTranscriptOperation::UnmaskedTimesMasked(unmasked_transcript, random_transcript), - rng, - ); - let multiplication_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&multiplication_params, rng); + let multiplication_params = build_params_from_previous( + random_params, + IDkgTranscriptOperation::UnmaskedTimesMasked( + unmasked_transcript, + random_transcript, + ), + rng, + ); + let multiplication_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&multiplication_params, rng); - assert_eq!( - multiplication_transcript.verified_dealings.len(), - multiplication_params.collection_threshold().get() as usize - ); + assert_eq!( + multiplication_transcript.verified_dealings.len(), + multiplication_params.collection_threshold().get() as usize + ); + } } #[test] @@ -1197,21 +1135,10 @@ mod verify_transcript { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + generate_presig_quadruple(&env, &dealers, &receivers, alg, &key_transcript, rng); + } } #[test] @@ -1236,32 +1163,29 @@ mod verify_transcript { rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let dealers = env - .nodes - .dealers(¶ms) - .take(params.collection_threshold().get() as usize) - .choose_multiple(rng, 2); + let dealers = env + .nodes + .dealers(¶ms) + .take(params.collection_threshold().get() as usize) + .choose_multiple(rng, 2); - let transcript = - swap_two_dealings_in_transcript(¶ms, transcript, &env, dealers[0], dealers[1]); + let transcript = + swap_two_dealings_in_transcript(¶ms, transcript, &env, dealers[0], dealers[1]); - let r = env - .nodes - .random_receiver(params.receivers(), rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_receiver(params.receivers(), rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, Ok(())); + assert_matches!(r, Ok(())); + } } //TODO CRP-2110: This test is currently ignored because it's flaky and the fix is not trivial. @@ -1280,42 +1204,39 @@ mod verify_transcript { rng, ); - let masked_key_params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let masked_key_params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let masked_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); + let masked_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); - let params = build_params_from_previous( - masked_key_params, - IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript), - rng, - ); + let params = build_params_from_previous( + masked_key_params, + IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript), + rng, + ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let dealers = env - .nodes - .dealers(¶ms) - .take(params.collection_threshold().get() as usize) - .choose_multiple(rng, 2); + let dealers = env + .nodes + .dealers(¶ms) + .take(params.collection_threshold().get() as usize) + .choose_multiple(rng, 2); - let transcript = - swap_two_dealings_in_transcript(¶ms, transcript, &env, dealers[0], dealers[1]); + let transcript = + swap_two_dealings_in_transcript(¶ms, transcript, &env, dealers[0], dealers[1]); - let r = env - .nodes - .random_receiver(params.receivers(), rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_receiver(params.receivers(), rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, Err(IDkgVerifyTranscriptError::InvalidTranscript)); + assert_matches!(r, Err(IDkgVerifyTranscriptError::InvalidTranscript)); + } } #[test] @@ -1332,54 +1253,51 @@ mod verify_transcript { rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let dealers = env - .nodes - .dealers(¶ms) - .take(params.collection_threshold().get() as usize) - .choose_multiple(rng, 2); - let dealer0 = dealers[0]; - let dealer1 = dealers[1]; - let dealer0_idx = transcript.index_for_dealer_id(dealer0.id()).unwrap(); - let dealer1_idx = transcript.index_for_dealer_id(dealer1.id()).unwrap(); - - let dealing = transcript - .verified_dealings - .get(&dealer0_idx) - .expect("Dealing exists") - .clone(); - - let dealing_resigned = dealing - .content - .into_builder() - .with_dealer_id(dealer1.id()) - .build_with_signature(¶ms, dealer1, dealer1.id()); - - let dealing = env - .nodes - .support_dealing_from_all_receivers(dealing_resigned, ¶ms); + let dealers = env + .nodes + .dealers(¶ms) + .take(params.collection_threshold().get() as usize) + .choose_multiple(rng, 2); + let dealer0 = dealers[0]; + let dealer1 = dealers[1]; + let dealer0_idx = transcript.index_for_dealer_id(dealer0.id()).unwrap(); + let dealer1_idx = transcript.index_for_dealer_id(dealer1.id()).unwrap(); + + let dealing = transcript + .verified_dealings + .get(&dealer0_idx) + .expect("Dealing exists") + .clone(); - assert!(transcript - .verified_dealings - .insert(dealer1_idx, dealing) - .is_some()); + let dealing_resigned = dealing + .content + .into_builder() + .with_dealer_id(dealer1.id()) + .build_with_signature(¶ms, dealer1, dealer1.id()); - let r = env - .nodes - .random_receiver(params.receivers(), rng) - .verify_transcript(¶ms, &transcript); + let dealing = env + .nodes + .support_dealing_from_all_receivers(dealing_resigned, ¶ms); + + assert!(transcript + .verified_dealings + .insert(dealer1_idx, dealing) + .is_some()); - assert_matches!(r, Err(IDkgVerifyTranscriptError::InvalidTranscript)); + let r = env + .nodes + .random_receiver(params.receivers(), rng) + .verify_transcript(¶ms, &transcript); + + assert_matches!(r, Err(IDkgVerifyTranscriptError::InvalidTranscript)); + } } #[test] @@ -1396,37 +1314,38 @@ mod verify_transcript { rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let dealings_to_remove = - 1 + (transcript.verified_dealings.len() - params.collection_threshold().get() as usize); + let dealings_to_remove = 1 + + (transcript.verified_dealings.len() + - params.collection_threshold().get() as usize); - let transcript = transcript - .into_builder() - .remove_some_dealings(dealings_to_remove) - .build(); + let transcript = transcript + .into_builder() + .remove_some_dealings(dealings_to_remove) + .build(); - assert!(transcript.verified_dealings.len() < params.collection_threshold().get() as usize); + assert!( + transcript.verified_dealings.len() < params.collection_threshold().get() as usize + ); - let r = env - .nodes - .random_receiver(params.receivers(), rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_receiver(params.receivers(), rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, Err(IDkgVerifyTranscriptError::InvalidArgument(msg)) - if msg.starts_with("failed to verify transcript against params: insufficient number of dealings")); + assert_matches!(r, Err(IDkgVerifyTranscriptError::InvalidArgument(msg)) + if msg.starts_with("failed to verify transcript against params: insufficient number of dealings")); + } } fn setup_for_verify_transcript_with_min_num_receivers( + alg: AlgorithmId, rng: &mut ReproducibleRng, subnet_size: usize, min_num_receivers: usize, @@ -1444,12 +1363,7 @@ mod verify_transcript { rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); let transcript = env .nodes @@ -1459,6 +1373,7 @@ mod verify_transcript { } fn setup_for_verify_transcript( + alg: AlgorithmId, rng: &mut ReproducibleRng, subnet_size: usize, ) -> ( @@ -1466,7 +1381,7 @@ mod verify_transcript { IDkgTranscriptParams, IDkgTranscript, ) { - setup_for_verify_transcript_with_min_num_receivers(rng, subnet_size, 1) + setup_for_verify_transcript_with_min_num_receivers(alg, rng, subnet_size, 1) } #[test] @@ -1474,28 +1389,30 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(4..10); - let (env, params, transcript) = setup_for_verify_transcript(&mut rng, subnet_size); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = setup_for_verify_transcript(alg, &mut rng, subnet_size); - let transcript = transcript - .into_builder() - .corrupt_internal_transcript_raw(&mut rng) - .build(); + let transcript = transcript + .into_builder() + .corrupt_internal_transcript_raw(&mut rng) + .build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - // Since the corruption is randomized, we might corrupt the CBOR or the commitments - // and thus different errors may result - match r { - Err(IDkgVerifyTranscriptError::InvalidTranscript) => {} + // Since the corruption is randomized, we might corrupt the CBOR or the commitments + // and thus different errors may result + match r { + Err(IDkgVerifyTranscriptError::InvalidTranscript) => {} - Err(IDkgVerifyTranscriptError::SerializationError(msg)) => { - assert!(msg.starts_with("failed to deserialize internal transcript")) + Err(IDkgVerifyTranscriptError::SerializationError(msg)) => { + assert!(msg.starts_with("failed to deserialize internal transcript")) + } + Err(e) => panic!("Unexpected error {:?}", e), + Ok(()) => panic!("Unexpected success"), } - Err(e) => panic!("Unexpected error {:?}", e), - Ok(()) => panic!("Unexpected success"), } } @@ -1504,18 +1421,20 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(4..10); - let (env, params, transcript) = setup_for_verify_transcript(&mut rng, subnet_size); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = setup_for_verify_transcript(alg, &mut rng, subnet_size); - let transcript = transcript.into_builder().corrupt_transcript_id().build(); + let transcript = transcript.into_builder().corrupt_transcript_id().build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, - Err(IDkgVerifyTranscriptError::InvalidArgument(e)) - if e.contains("mismatching transcript IDs in transcript")); + assert_matches!(r, + Err(IDkgVerifyTranscriptError::InvalidArgument(e)) + if e.contains("mismatching transcript IDs in transcript")); + } } #[test] @@ -1523,18 +1442,20 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(4..10); - let (env, params, transcript) = setup_for_verify_transcript(&mut rng, subnet_size); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = setup_for_verify_transcript(alg, &mut rng, subnet_size); - let transcript = transcript.into_builder().corrupt_registry_version().build(); + let transcript = transcript.into_builder().corrupt_registry_version().build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, - Err(IDkgVerifyTranscriptError::InvalidArgument(e)) - if e.contains("mismatching registry versions in transcript")); + assert_matches!(r, + Err(IDkgVerifyTranscriptError::InvalidArgument(e)) + if e.contains("mismatching registry versions in transcript")); + } } #[test] @@ -1542,18 +1463,20 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(4..10); - let (env, params, transcript) = setup_for_verify_transcript(&mut rng, subnet_size); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = setup_for_verify_transcript(alg, &mut rng, subnet_size); - let transcript = transcript.into_builder().corrupt_algorithm_id().build(); + let transcript = transcript.into_builder().corrupt_algorithm_id().build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, - Err(IDkgVerifyTranscriptError::InvalidArgument(e)) - if e.contains("mismatching algorithm IDs in transcript")); + assert_matches!(r, + Err(IDkgVerifyTranscriptError::InvalidArgument(e)) + if e.contains("mismatching algorithm IDs in transcript")); + } } #[test] @@ -1561,18 +1484,20 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(4..10); - let (env, params, transcript) = setup_for_verify_transcript(&mut rng, subnet_size); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = setup_for_verify_transcript(alg, &mut rng, subnet_size); - let transcript = transcript.into_builder().add_a_new_receiver().build(); + let transcript = transcript.into_builder().add_a_new_receiver().build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, - Err(IDkgVerifyTranscriptError::InvalidArgument(e)) - if e.contains("mismatching receivers in transcript")); + assert_matches!(r, + Err(IDkgVerifyTranscriptError::InvalidArgument(e)) + if e.contains("mismatching receivers in transcript")); + } } #[test] @@ -1580,19 +1505,21 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(6..10); - let (env, params, transcript) = - setup_for_verify_transcript_with_min_num_receivers(&mut rng, subnet_size, 2); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = + setup_for_verify_transcript_with_min_num_receivers(alg, &mut rng, subnet_size, 2); - let transcript = transcript.into_builder().remove_a_receiver().build(); + let transcript = transcript.into_builder().remove_a_receiver().build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, - Err(IDkgVerifyTranscriptError::InvalidArgument(e)) - if e.contains("mismatching receivers in transcript")); + assert_matches!(r, + Err(IDkgVerifyTranscriptError::InvalidArgument(e)) + if e.contains("mismatching receivers in transcript")); + } } #[test] @@ -1600,18 +1527,20 @@ mod verify_transcript { let mut rng = reproducible_rng(); let subnet_size = rng.gen_range(4..10); - let (env, params, transcript) = setup_for_verify_transcript(&mut rng, subnet_size); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, transcript) = setup_for_verify_transcript(alg, &mut rng, subnet_size); - let transcript = transcript.into_builder().corrupt_transcript_type().build(); + let transcript = transcript.into_builder().corrupt_transcript_type().build(); - let r = env - .nodes - .random_node(&mut rng) - .verify_transcript(¶ms, &transcript); + let r = env + .nodes + .random_node(&mut rng) + .verify_transcript(¶ms, &transcript); - assert_matches!(r, - Err(IDkgVerifyTranscriptError::InvalidArgument(e)) - if e.contains("failed to verify transcript against params: transcript's type")); + assert_matches!(r, + Err(IDkgVerifyTranscriptError::InvalidArgument(e)) + if e.contains("failed to verify transcript against params: transcript's type")); + } } } @@ -1633,45 +1562,35 @@ mod sign_share { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let quadruple = + generate_presig_quadruple(&env, &dealers, &receivers, alg, &key_transcript, rng); - let inputs = { - let derivation_path = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], - }; + let inputs = { + let derivation_path = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }; - let hashed_message = rng.gen::<[u8; 32]>(); - let seed = Randomness::from(rng.gen::<[u8; 32]>()); + let hashed_message = rng.gen::<[u8; 32]>(); + let seed = Randomness::from(rng.gen::<[u8; 32]>()); - ThresholdEcdsaSigInputs::new( - &derivation_path, - &hashed_message, - seed, - quadruple, - key_transcript, - ) - .expect("failed to create signature inputs") - }; + ThresholdEcdsaSigInputs::new( + &derivation_path, + &hashed_message, + seed, + quadruple, + key_transcript, + ) + .expect("failed to create signature inputs") + }; - let receiver = env.nodes.random_receiver(inputs.receivers(), rng); - receiver.load_input_transcripts(&inputs); - let result = receiver.sign_share(&inputs); - assert_matches!(result, Ok(_)); + let receiver = env.nodes.random_receiver(inputs.receivers(), rng); + receiver.load_input_transcripts(&inputs); + let result = receiver.sign_share(&inputs); + assert_matches!(result, Ok(_)); + } } #[test] @@ -1679,84 +1598,149 @@ mod sign_share { let rng = &mut reproducible_rng(); let subnet_size: usize = 1; - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); - let (dealers, receivers) = - env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); + let (dealers, receivers) = env + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let inputs = { - let derivation_path = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let quadruple = + generate_presig_quadruple(&env, &dealers, &receivers, alg, &key_transcript, rng); + + let inputs = { + let derivation_path = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }; + + let hashed_message = rng.gen::<[u8; 32]>(); + let seed = Randomness::from(rng.gen::<[u8; 32]>()); + + ThresholdEcdsaSigInputs::new( + &derivation_path, + &hashed_message, + seed, + quadruple, + key_transcript, + ) + .expect("failed to create signature inputs") }; - let hashed_message = rng.gen::<[u8; 32]>(); - let seed = Randomness::from(rng.gen::<[u8; 32]>()); + let signer = env.nodes.into_random_receiver(inputs.receivers(), rng); - ThresholdEcdsaSigInputs::new( - &derivation_path, - &hashed_message, - seed, - quadruple, - key_transcript, - ) - .expect("failed to create signature inputs") - }; + signer.load_input_transcripts(&inputs); + + let _result = signer.sign_share(&inputs); + let logs = signer.drain_logs(); + LogEntriesAssert::assert_that(logs) + .has_only_one_message_containing(&Level::Info, "MASTER tECDSA PUBLIC KEY: "); + } + } + + #[test] + fn should_log_same_public_key_successfully_for_multiple_quadruples_and_inputs() { + let rng = &mut reproducible_rng(); + + const SUBNET_SIZE: usize = 1; + const NUM_SIGNATURES: usize = 2; + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(SUBNET_SIZE, rng); + let (dealers, receivers) = env + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let mut inputs: Vec = Vec::new(); + for _ in 0..NUM_SIGNATURES { + let quadruple = generate_presig_quadruple( + &env, + &dealers, + &receivers, + alg, + &key_transcript, + rng, + ); + + let sig_inputs = { + let derivation_path = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }; + + let hashed_message = rng.gen::<[u8; 32]>(); + let seed = Randomness::from(rng.gen::<[u8; 32]>()); + + ThresholdEcdsaSigInputs::new( + &derivation_path, + &hashed_message, + seed, + quadruple, + key_transcript.clone(), + ) + .expect("failed to create signature inputs") + }; + inputs.push(sig_inputs); + } + + let first_input = inputs.first().expect("missing inputs"); + let signer = env.nodes.into_random_receiver(first_input.receivers(), rng); + signer.load_input_transcripts(first_input); - let signer = env.nodes.into_random_receiver(inputs.receivers(), rng); + for i in 0..NUM_SIGNATURES { + let _result = signer.sign_share(inputs.get(i).expect("missing input")); + } - signer.load_input_transcripts(&inputs); + let logs = signer.drain_logs(); + let logged_public_keys = parse_logged_public_keys(&logs); + assert_eq!(NUM_SIGNATURES, logged_public_keys.len()); + let first_public_key = logged_public_keys + .first() + .expect("missing logged public key"); + assert!(first_public_key.contains("MASTER tECDSA PUBLIC KEY: ")); + for i in 1..NUM_SIGNATURES { + assert_eq!( + first_public_key, + logged_public_keys + .get(i) + .expect("missing logged public key") + ) + } + } + } - let _result = signer.sign_share(&inputs); - let logs = signer.drain_logs(); - LogEntriesAssert::assert_that(logs) - .has_only_one_message_containing(&Level::Info, "MASTER tECDSA PUBLIC KEY: "); + fn parse_logged_public_keys(logs: &Vec) -> Vec { + let mut logged_public_keys: Vec = Vec::new(); + for log in logs { + if log.message.contains("MASTER tECDSA PUBLIC KEY: ") { + logged_public_keys.push(log.message.clone()); + } + } + logged_public_keys } #[test] - fn should_log_same_public_key_successfully_for_multiple_quadruples_and_inputs() { - let rng = &mut reproducible_rng(); + fn should_fail_create_signature_if_not_receiver() { + let mut rng = reproducible_rng(); - const SUBNET_SIZE: usize = 1; - const NUM_SIGNATURES: usize = 2; - let env = CanisterThresholdSigTestEnvironment::new(SUBNET_SIZE, rng); - let (dealers, receivers) = - env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let subnet_size = rng.gen_range(1..10); + let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); + let (dealers, receivers) = env.choose_dealers_and_receivers( + &IDkgParticipants::RandomForThresholdSignature, + &mut rng, + ); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut inputs: Vec = Vec::new(); - for _ in 0..NUM_SIGNATURES { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, &mut rng); let quadruple = generate_presig_quadruple( &env, &dealers, &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, + alg, &key_transcript, - rng, + &mut rng, ); - let sig_inputs = { + let inputs = { let derivation_path = ExtendedDerivationPath { caller: PrincipalId::new_user_test_id(1), derivation_path: vec![], @@ -1770,101 +1754,22 @@ mod sign_share { &hashed_message, seed, quadruple, - key_transcript.clone(), + key_transcript, ) .expect("failed to create signature inputs") }; - inputs.push(sig_inputs); - } - let first_input = inputs.first().expect("missing inputs"); - let signer = env.nodes.into_random_receiver(first_input.receivers(), rng); - signer.load_input_transcripts(first_input); + let bad_signer_id = random_node_id_excluding(inputs.receivers().get(), &mut rng); + let bad_crypto_component = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(bad_signer_id) + .with_rng(rng.fork()) + .build(); - for i in 0..NUM_SIGNATURES { - let _result = signer.sign_share(inputs.get(i).expect("missing input")); - } - - let logs = signer.drain_logs(); - let logged_public_keys = parse_logged_public_keys(&logs); - assert_eq!(NUM_SIGNATURES, logged_public_keys.len()); - let first_public_key = logged_public_keys - .first() - .expect("missing logged public key"); - assert!(first_public_key.contains("MASTER tECDSA PUBLIC KEY: ")); - for i in 1..NUM_SIGNATURES { - assert_eq!( - first_public_key, - logged_public_keys - .get(i) - .expect("missing logged public key") - ) - } - } - - fn parse_logged_public_keys(logs: &Vec) -> Vec { - let mut logged_public_keys: Vec = Vec::new(); - for log in logs { - if log.message.contains("MASTER tECDSA PUBLIC KEY: ") { - logged_public_keys.push(log.message.clone()); - } + let result = bad_crypto_component.sign_share(&inputs); + let err = result.unwrap_err(); + assert_matches!(err, ThresholdEcdsaSignShareError::NotAReceiver); } - logged_public_keys - } - - #[test] - fn should_fail_create_signature_if_not_receiver() { - let mut rng = reproducible_rng(); - let subnet_size = rng.gen_range(1..10); - let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); - let (dealers, receivers) = env - .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, &mut rng); - - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - &mut rng, - ); - - let inputs = { - let derivation_path = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], - }; - - let hashed_message = rng.gen::<[u8; 32]>(); - let seed = Randomness::from(rng.gen::<[u8; 32]>()); - - ThresholdEcdsaSigInputs::new( - &derivation_path, - &hashed_message, - seed, - quadruple, - key_transcript, - ) - .expect("failed to create signature inputs") - }; - - let bad_signer_id = random_node_id_excluding(inputs.receivers().get(), &mut rng); - let bad_crypto_component = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(bad_signer_id) - .with_rng(rng) - .build(); - - let result = bad_crypto_component.sign_share(&inputs); - let err = result.unwrap_err(); - assert_matches!(err, ThresholdEcdsaSignShareError::NotAReceiver); } #[test] @@ -1875,62 +1780,47 @@ mod sign_share { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let quadruple = + generate_presig_quadruple(&env, &dealers, &receivers, alg, &key_transcript, rng); - let inputs = { - let derivation_path = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], - }; + let inputs = { + let derivation_path = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }; - let hashed_message = rng.gen::<[u8; 32]>(); - let seed = Randomness::from(rng.gen::<[u8; 32]>()); + let hashed_message = rng.gen::<[u8; 32]>(); + let seed = Randomness::from(rng.gen::<[u8; 32]>()); - ThresholdEcdsaSigInputs::new( - &derivation_path, - &hashed_message, - seed, - quadruple, - key_transcript, - ) - .expect("failed to create signature inputs") - }; + ThresholdEcdsaSigInputs::new( + &derivation_path, + &hashed_message, + seed, + quadruple, + key_transcript, + ) + .expect("failed to create signature inputs") + }; - let receiver = env.nodes.random_receiver(inputs.receivers(), rng); - receiver.load_input_transcripts(&inputs); - assert_matches!(receiver.sign_share(&inputs), Ok(_)); - let another_key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let active_transcripts = hashset!(another_key_transcript); - assert_eq!( - receiver.retain_active_transcripts(&active_transcripts), - Ok(()) - ); + let receiver = env.nodes.random_receiver(inputs.receivers(), rng); + receiver.load_input_transcripts(&inputs); + assert_matches!(receiver.sign_share(&inputs), Ok(_)); + let another_key_transcript = + generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let active_transcripts = hashset!(another_key_transcript); + assert_eq!( + receiver.retain_active_transcripts(&active_transcripts), + Ok(()) + ); - let result = receiver.sign_share(&inputs); - assert_matches!( - result, - Err(ThresholdEcdsaSignShareError::SecretSharesNotFound { .. }) - ); + let result = receiver.sign_share(&inputs); + assert_matches!( + result, + Err(ThresholdEcdsaSignShareError::SecretSharesNotFound { .. }) + ); + } } #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] @@ -2020,95 +1910,93 @@ mod sign_share { caller: PrincipalId::new_user_test_id(1), derivation_path: vec![], }; - let hashed_message = rng.gen::<[u8; 32]>(); - let seed = Randomness::from(rng.gen::<[u8; 32]>()); - - const CHACHA_SEED_LEN: usize = 32; - let mut runner = TestRunner::new_with_rng( - Config::with_cases(10), - TestRng::from_seed(RngAlgorithm::ChaCha, &rng.gen::<[u8; CHACHA_SEED_LEN]>()), - ); - // retain_active_transcripts is a no-op when the parameter active_transcripts is empty - let result = runner.run( - &( - arb_signer_state_with_at_least_one_retained_transcript(), - vec(0..=255u8, CHACHA_SEED_LEN), - ), - |(signer_state, rng_seed)| { - let mut inner_rng = ChaCha20Rng::from_seed( - rng_seed[..] - .try_into() - .expect("Failed to convert seed to array"), - ); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut inner_rng, - ); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - &mut inner_rng, - ); - - let inputs = ThresholdEcdsaSigInputs::new( - &derivation_path, - &hashed_message, - seed, - quadruple, - key_transcript, - ) - .expect("failed to create signature inputs"); - let receiver = env - .nodes - .random_receiver(inputs.receivers(), &mut inner_rng); - receiver.load_input_transcripts(&inputs); - assert_matches!( - receiver.sign_share(&inputs), - Ok(_), - "{} failed to sign share with all transcripts loaded for state {:?}", - receiver.id(), - signer_state - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let hashed_message = rng.gen::<[u8; 32]>(); + let seed = Randomness::from(rng.gen::<[u8; 32]>()); - let active_transcripts = signer_state.transcripts_to_retain(&inputs); - assert_eq!( - receiver.retain_active_transcripts(&active_transcripts), - Ok(()), - "{} failed to retain transcripts specified in {:?}", - receiver.id(), - signer_state - ); + const CHACHA_SEED_LEN: usize = 32; + let mut runner = TestRunner::new_with_rng( + Config::with_cases(10), + TestRng::from_seed(RngAlgorithm::ChaCha, &rng.gen::<[u8; CHACHA_SEED_LEN]>()), + ); + // retain_active_transcripts is a no-op when the parameter active_transcripts is empty + let result = runner.run( + &( + arb_signer_state_with_at_least_one_retained_transcript(), + vec(0..=255u8, CHACHA_SEED_LEN), + ), + |(signer_state, rng_seed)| { + let mut inner_rng = ChaCha20Rng::from_seed( + rng_seed[..] + .try_into() + .expect("Failed to convert seed to array"), + ); + let key_transcript = + generate_key_transcript(&env, &dealers, &receivers, alg, &mut inner_rng); + let quadruple = generate_presig_quadruple( + &env, + &dealers, + &receivers, + alg, + &key_transcript, + &mut inner_rng, + ); - let result = receiver.sign_share(&inputs); + let inputs = ThresholdEcdsaSigInputs::new( + &derivation_path, + &hashed_message, + seed, + quadruple, + key_transcript, + ) + .expect("failed to create signature inputs"); - if signer_state.should_be_able_to_sign_share() { + let receiver = env + .nodes + .random_receiver(inputs.receivers(), &mut inner_rng); + receiver.load_input_transcripts(&inputs); assert_matches!( - result, + receiver.sign_share(&inputs), Ok(_), - "{} should have been able to sign a share with state {:?}", + "{} failed to sign share with all transcripts loaded for state {:?}", receiver.id(), signer_state ); - } else { - assert_matches!( - result, - Err(ThresholdEcdsaSignShareError::SecretSharesNotFound { .. }), - "{} should not have been able to sign a share with state {:?}", + + let active_transcripts = signer_state.transcripts_to_retain(&inputs); + assert_eq!( + receiver.retain_active_transcripts(&active_transcripts), + Ok(()), + "{} failed to retain transcripts specified in {:?}", receiver.id(), signer_state ); - } - Ok(()) - }, - ); - assert_eq!(result, Ok(())); + + let result = receiver.sign_share(&inputs); + + if signer_state.should_be_able_to_sign_share() { + assert_matches!( + result, + Ok(_), + "{} should have been able to sign a share with state {:?}", + receiver.id(), + signer_state + ); + } else { + assert_matches!( + result, + Err(ThresholdEcdsaSignShareError::SecretSharesNotFound { .. }), + "{} should not have been able to sign a share with state {:?}", + receiver.id(), + signer_state + ); + } + Ok(()) + }, + ); + assert_eq!(result, Ok(())); + } } } @@ -2121,174 +2009,190 @@ mod verify_sig_share { #[test] fn should_verify_sig_share_successfully() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let result = verifier.verify_sig_share(signer_id, &inputs, &sig_share); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + + let result = verifier.verify_sig_share(signer_id, &inputs, &sig_share); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] fn should_fail_verifying_inputs_with_wrong_hashed_message() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let inputs_with_wrong_hash = inputs - .clone() - .into_builder() - .corrupt_hashed_message() - .build(); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - - let result = verifier.verify_sig_share(signer_id, &inputs_with_wrong_hash, &sig_share); - - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let inputs_with_wrong_hash = inputs + .clone() + .into_builder() + .corrupt_hashed_message() + .build(); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + + let result = verifier.verify_sig_share(signer_id, &inputs_with_wrong_hash, &sig_share); + + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) + ); + } } #[test] fn should_fail_verifying_inputs_with_wrong_nonce() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let inputs_with_wrong_nonce = inputs.clone().into_builder().corrupt_nonce().build(); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let inputs_with_wrong_nonce = inputs.clone().into_builder().corrupt_nonce().build(); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let result = verifier.verify_sig_share(signer_id, &inputs_with_wrong_nonce, &sig_share); + let result = verifier.verify_sig_share(signer_id, &inputs_with_wrong_nonce, &sig_share); - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) - ); + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) + ); + } } #[test] fn should_fail_verifying_corrupted_sig_share() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let (signer_id, corrupted_sig_share) = { - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - (signer_id, sig_share.clone_with_bit_flipped()) - }; - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let (signer_id, corrupted_sig_share) = { + let (signer_id, sig_share) = + signature_share_from_random_receiver(&env, &inputs, rng); + (signer_id, sig_share.clone_with_bit_flipped()) + }; + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let result = verifier.verify_sig_share(signer_id, &inputs, &corrupted_sig_share); + let result = verifier.verify_sig_share(signer_id, &inputs, &corrupted_sig_share); - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) - ); + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) + ); + } } #[test] fn should_verify_sig_share_from_another_signer_when_threshold_1() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(2..=3, rng); - assert_eq!(inputs.key_transcript().reconstruction_threshold().get(), 1); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let other_signer_id = random_receiver_id_excluding(inputs.receivers(), signer_id, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(2..=3, alg, rng); + assert_eq!(inputs.key_transcript().reconstruction_threshold().get(), 1); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let other_signer_id = random_receiver_id_excluding(inputs.receivers(), signer_id, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let result = verifier.verify_sig_share(other_signer_id, &inputs, &sig_share); + let result = verifier.verify_sig_share(other_signer_id, &inputs, &sig_share); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] fn should_fail_verifying_sig_share_from_another_signer() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(4..10, rng); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let other_signer_id = random_receiver_id_excluding(inputs.receivers(), signer_id, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(4..10, alg, rng); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let other_signer_id = random_receiver_id_excluding(inputs.receivers(), signer_id, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let result = verifier.verify_sig_share(other_signer_id, &inputs, &sig_share); + let result = verifier.verify_sig_share(other_signer_id, &inputs, &sig_share); - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) - ); + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) + ); + } } #[test] fn should_fail_verifying_sig_share_for_unknown_signer() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let unknown_signer_id = NodeId::from(PrincipalId::new_node_test_id(1)); - assert_ne!(signer_id, unknown_signer_id); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - - let result = verifier.verify_sig_share(unknown_signer_id, &inputs, &sig_share); - - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::InvalidArgumentMissingSignerInTranscript {signer_id}) - if signer_id == unknown_signer_id - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let unknown_signer_id = NodeId::from(PrincipalId::new_node_test_id(1)); + assert_ne!(signer_id, unknown_signer_id); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + + let result = verifier.verify_sig_share(unknown_signer_id, &inputs, &sig_share); + + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::InvalidArgumentMissingSignerInTranscript {signer_id}) + if signer_id == unknown_signer_id + ); + } } #[test] fn should_fail_deserializing_sig_share() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let signer_id = random_receiver_for_inputs(&inputs, rng); - let invalid_sig_share = ThresholdEcdsaSigShare { - sig_share_raw: Vec::new(), - }; + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + let signer_id = random_receiver_for_inputs(&inputs, rng); + let invalid_sig_share = ThresholdEcdsaSigShare { + sig_share_raw: Vec::new(), + }; - let result = verifier.verify_sig_share(signer_id, &inputs, &invalid_sig_share); + let result = verifier.verify_sig_share(signer_id, &inputs, &invalid_sig_share); - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::SerializationError { .. }) - ) + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::SerializationError { .. }) + ) + } } #[test] fn should_fail_when_key_internal_transcript_raw_switched() { let rng = &mut reproducible_rng(); - let (env, inputs, dealers, receivers) = environment_with_sig_inputs(1..10, rng); - let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); - let verifier = env.nodes.random_receiver(inputs.receivers(), rng); - let inputs_with_other_key_internal_transcript_raw = { - let another_key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - assert_ne!(inputs.key_transcript(), &another_key_transcript); - let key_transcript_with_other_internal_raw = IDkgTranscript { - internal_transcript_raw: another_key_transcript.internal_transcript_raw, - ..inputs.key_transcript().clone() + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, dealers, receivers) = environment_with_sig_inputs(1..10, alg, rng); + let (signer_id, sig_share) = signature_share_from_random_receiver(&env, &inputs, rng); + let verifier = env.nodes.random_receiver(inputs.receivers(), rng); + let inputs_with_other_key_internal_transcript_raw = { + let another_key_transcript = + generate_key_transcript(&env, &dealers, &receivers, alg, rng); + assert_ne!(inputs.key_transcript(), &another_key_transcript); + let key_transcript_with_other_internal_raw = IDkgTranscript { + internal_transcript_raw: another_key_transcript.internal_transcript_raw, + ..inputs.key_transcript().clone() + }; + ThresholdEcdsaSigInputs::new( + inputs.derivation_path(), + inputs.hashed_message(), + *inputs.nonce(), + inputs.presig_quadruple().clone(), + key_transcript_with_other_internal_raw, + ) + .expect("invalid ECDSA inputs") }; - ThresholdEcdsaSigInputs::new( - inputs.derivation_path(), - inputs.hashed_message(), - *inputs.nonce(), - inputs.presig_quadruple().clone(), - key_transcript_with_other_internal_raw, - ) - .expect("invalid ECDSA inputs") - }; - let result = verifier.verify_sig_share( - signer_id, - &inputs_with_other_key_internal_transcript_raw, - &sig_share, - ); + let result = verifier.verify_sig_share( + signer_id, + &inputs_with_other_key_internal_transcript_raw, + &sig_share, + ); - assert_matches!( - result, - Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) - ); + assert_matches!( + result, + Err(ThresholdEcdsaVerifySigShareError::InvalidSignatureShare) + ); + } } fn signature_share_from_random_receiver( @@ -2339,23 +2243,20 @@ mod retain_active_transcripts { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let retainer = env.nodes.random_receiver(params.receivers(), rng); + let retainer = env.nodes.random_receiver(params.receivers(), rng); - let active_transcripts = hashset!(transcript); - assert_eq!( - retainer.retain_active_transcripts(&active_transcripts), - Ok(()) - ); + let active_transcripts = hashset!(transcript); + assert_eq!( + retainer.retain_active_transcripts(&active_transcripts), + Ok(()) + ); + } } } @@ -2370,21 +2271,19 @@ mod load_transcript_with_openings { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let loader = env.nodes.random_receiver(params.receivers(), rng); - let openings = BTreeMap::new(); - let result = loader.load_transcript_with_openings(&transcript, &openings); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let loader = env.nodes.random_receiver(params.receivers(), rng); + let openings = BTreeMap::new(); + + let result = loader.load_transcript_with_openings(&transcript, &openings); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] @@ -2400,33 +2299,31 @@ mod load_transcript_with_openings { }, rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let reconstruction_threshold = - usize::try_from(transcript.reconstruction_threshold().get()).expect("invalid number"); - let number_of_openings = reconstruction_threshold; - - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let complaint_with_openings = generate_and_verify_openings_for_complaint( - number_of_openings, - &transcript, - &env, - complainer, - complaint, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let reconstruction_threshold = + usize::try_from(transcript.reconstruction_threshold().get()) + .expect("invalid number"); + let number_of_openings = reconstruction_threshold; + + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let complaint_with_openings = generate_and_verify_openings_for_complaint( + number_of_openings, + &transcript, + &env, + complainer, + complaint, + ); - let result = - complainer.load_transcript_with_openings(&transcript, &complaint_with_openings); + let result = + complainer.load_transcript_with_openings(&transcript, &complaint_with_openings); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] @@ -2442,36 +2339,34 @@ mod load_transcript_with_openings { }, rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let mut transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms, rng); - let reconstruction_threshold = - usize::try_from(transcript.reconstruction_threshold().get()).expect("invalid number"); - let number_of_openings = reconstruction_threshold - 1; - - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let complaint_with_openings = generate_and_verify_openings_for_complaint( - number_of_openings, - &transcript, - &env, - complainer, - complaint, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let mut transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms, rng); + let reconstruction_threshold = + usize::try_from(transcript.reconstruction_threshold().get()) + .expect("invalid number"); + let number_of_openings = reconstruction_threshold - 1; + + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let complaint_with_openings = generate_and_verify_openings_for_complaint( + number_of_openings, + &transcript, + &env, + complainer, + complaint, + ); - let result = - complainer.load_transcript_with_openings(&transcript, &complaint_with_openings); + let result = + complainer.load_transcript_with_openings(&transcript, &complaint_with_openings); - assert_matches!( - result, - Err(IDkgLoadTranscriptError::InsufficientOpenings { .. }) - ); + assert_matches!( + result, + Err(IDkgLoadTranscriptError::InsufficientOpenings { .. }) + ); + } } fn generate_and_verify_openings_for_complaint( @@ -2523,32 +2418,36 @@ mod combine_sig_shares { #[test] fn should_combine_sig_shares_successfully() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let sig_shares = sig_share_from_each_receiver(&env, &inputs); - let combiner = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let sig_shares = sig_share_from_each_receiver(&env, &inputs); + let combiner = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let result = combiner.combine_sig_shares(&inputs, &sig_shares); + let result = combiner.combine_sig_shares(&inputs, &sig_shares); - assert_matches!(result, Ok(_)); + assert_matches!(result, Ok(_)); + } } #[test] fn should_fail_combining_sig_shares_with_insufficient_shares() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let insufficient_sig_shares = sig_share_from_each_receiver(&env, &inputs) - .into_iter() - .take(inputs.reconstruction_threshold().get() as usize - 1) - .collect(); - let combiner = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let insufficient_sig_shares = sig_share_from_each_receiver(&env, &inputs) + .into_iter() + .take(inputs.reconstruction_threshold().get() as usize - 1) + .collect(); + let combiner = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let result = combiner.combine_sig_shares(&inputs, &insufficient_sig_shares); + let result = combiner.combine_sig_shares(&inputs, &insufficient_sig_shares); - assert_matches!( - result, - Err(ThresholdEcdsaCombineSigSharesError::UnsatisfiedReconstructionThreshold {threshold, share_count}) - if threshold == inputs.reconstruction_threshold().get() && share_count == (threshold as usize - 1) - ); + assert_matches!( + result, + Err(ThresholdEcdsaCombineSigSharesError::UnsatisfiedReconstructionThreshold {threshold, share_count}) + if threshold == inputs.reconstruction_threshold().get() && share_count == (threshold as usize - 1) + ); + } } } @@ -2560,165 +2459,203 @@ mod verify_combined_sig { #[test] fn should_verify_combined_sig() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let sig_shares = sig_share_from_each_receiver(&env, &inputs); - let combiner_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let signature = combiner_crypto_component - .combine_sig_shares(&inputs, &sig_shares) - .expect("Failed to generate signature"); - let verifier_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - - let result = verifier_crypto_component.verify_combined_sig(&inputs, &signature); - - assert_eq!(result, Ok(())); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let sig_shares = sig_share_from_each_receiver(&env, &inputs); + let combiner_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + let signature = combiner_crypto_component + .combine_sig_shares(&inputs, &sig_shares) + .expect("Failed to generate signature"); + let verifier_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + + let result = verifier_crypto_component.verify_combined_sig(&inputs, &signature); + + assert_eq!(result, Ok(())); + } } #[test] fn should_fail_verifying_corrupted_combined_sig() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let sig_shares = sig_share_from_each_receiver(&env, &inputs); - let combiner_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let corrupted_signature = combiner_crypto_component - .combine_sig_shares(&inputs, &sig_shares) - .expect("Failed to generate signature") - .clone_with_bit_flipped(); - let verifier_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - - let result = verifier_crypto_component.verify_combined_sig(&inputs, &corrupted_signature); - - assert_matches!( - result, - Err(ThresholdEcdsaVerifyCombinedSignatureError::InvalidSignature) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let sig_shares = sig_share_from_each_receiver(&env, &inputs); + let combiner_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + let corrupted_signature = combiner_crypto_component + .combine_sig_shares(&inputs, &sig_shares) + .expect("Failed to generate signature") + .clone_with_bit_flipped(); + let verifier_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + + let result = + verifier_crypto_component.verify_combined_sig(&inputs, &corrupted_signature); + + assert_matches!( + result, + Err(ThresholdEcdsaVerifyCombinedSignatureError::InvalidSignature) + ); + } } #[test] fn should_fail_deserializing_signature_with_invalid_length() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let sig_shares = sig_share_from_each_receiver(&env, &inputs); - let combiner_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let mut corrupted_signature = combiner_crypto_component - .combine_sig_shares(&inputs, &sig_shares) - .expect("Failed to generate signature"); - corrupted_signature.signature.pop(); - let verifier_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - - let result = verifier_crypto_component.verify_combined_sig(&inputs, &corrupted_signature); - - assert_matches!( - result, - Err(ThresholdEcdsaVerifyCombinedSignatureError::SerializationError { .. }) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let sig_shares = sig_share_from_each_receiver(&env, &inputs); + let combiner_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + let mut corrupted_signature = combiner_crypto_component + .combine_sig_shares(&inputs, &sig_shares) + .expect("Failed to generate signature"); + corrupted_signature.signature.pop(); + let verifier_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + + let result = + verifier_crypto_component.verify_combined_sig(&inputs, &corrupted_signature); + + assert_matches!( + result, + Err(ThresholdEcdsaVerifyCombinedSignatureError::SerializationError { .. }) + ); + } } #[test] fn should_fail_when_key_internal_transcript_raw_switched() { let rng = &mut reproducible_rng(); - let (env, inputs, dealers, receivers) = environment_with_sig_inputs(1..10, rng); - let sig_shares = sig_share_from_each_receiver(&env, &inputs); - let combiner_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let signature = combiner_crypto_component - .combine_sig_shares(&inputs, &sig_shares) - .expect("Failed to generate signature"); - let verifier_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - - let inputs_with_other_key_internal_transcript_raw = { - let another_key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - assert_ne!(inputs.key_transcript(), &another_key_transcript); - let key_transcript_with_other_internal_raw = IDkgTranscript { - internal_transcript_raw: another_key_transcript.internal_transcript_raw, - ..inputs.key_transcript().clone() + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, dealers, receivers) = environment_with_sig_inputs(1..10, alg, rng); + let sig_shares = sig_share_from_each_receiver(&env, &inputs); + let combiner_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + let signature = combiner_crypto_component + .combine_sig_shares(&inputs, &sig_shares) + .expect("Failed to generate signature"); + let verifier_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + + let inputs_with_other_key_internal_transcript_raw = { + let another_key_transcript = + generate_key_transcript(&env, &dealers, &receivers, alg, rng); + assert_ne!(inputs.key_transcript(), &another_key_transcript); + let key_transcript_with_other_internal_raw = IDkgTranscript { + internal_transcript_raw: another_key_transcript.internal_transcript_raw, + ..inputs.key_transcript().clone() + }; + ThresholdEcdsaSigInputs::new( + inputs.derivation_path(), + inputs.hashed_message(), + *inputs.nonce(), + inputs.presig_quadruple().clone(), + key_transcript_with_other_internal_raw, + ) + .expect("invalid ECDSA inputs") }; - ThresholdEcdsaSigInputs::new( - inputs.derivation_path(), - inputs.hashed_message(), - *inputs.nonce(), - inputs.presig_quadruple().clone(), - key_transcript_with_other_internal_raw, - ) - .expect("invalid ECDSA inputs") - }; - let result = verifier_crypto_component - .verify_combined_sig(&inputs_with_other_key_internal_transcript_raw, &signature); + let result = verifier_crypto_component + .verify_combined_sig(&inputs_with_other_key_internal_transcript_raw, &signature); - assert_matches!( - result, - Err(ThresholdEcdsaVerifyCombinedSignatureError::InvalidSignature) - ); + assert_matches!( + result, + Err(ThresholdEcdsaVerifyCombinedSignatureError::InvalidSignature) + ); + } } #[test] fn should_fail_verifying_combined_sig_for_inputs_with_wrong_hash() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let sig_shares = sig_share_from_each_receiver(&env, &inputs); - let combiner = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - let signature = combiner - .combine_sig_shares(&inputs, &sig_shares) - .expect("Failed to generate signature"); - let verifier_crypto_component = - random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - - let result = verifier_crypto_component.verify_combined_sig( - &inputs.into_builder().corrupt_hashed_message().build(), - &signature, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let sig_shares = sig_share_from_each_receiver(&env, &inputs); + let combiner = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + let signature = combiner + .combine_sig_shares(&inputs, &sig_shares) + .expect("Failed to generate signature"); + let verifier_crypto_component = + random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + + let result = verifier_crypto_component.verify_combined_sig( + &inputs.into_builder().corrupt_hashed_message().build(), + &signature, + ); - assert_matches!( - result, - Err(ThresholdEcdsaVerifyCombinedSignatureError::InvalidSignature) - ); + assert_matches!( + result, + Err(ThresholdEcdsaVerifyCombinedSignatureError::InvalidSignature) + ); + } } #[test] fn should_run_threshold_ecdsa_protocol_with_single_node() { let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..=1, rng); - let signature = run_tecdsa_protocol(&env, &inputs, rng); - let verifier = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..=1, alg, rng); + let signature = run_tecdsa_protocol(&env, &inputs, rng); + let verifier = random_crypto_component_not_in_receivers(&env, inputs.receivers(), rng); - assert_eq!(verifier.verify_combined_sig(&inputs, &signature), Ok(())); + assert_eq!(verifier.verify_combined_sig(&inputs, &signature), Ok(())); + } } #[test] - fn should_verify_combined_signature_with_usual_secp256k1_operation() { + fn should_verify_combined_signature_with_usual_basic_sig_verification() { use ic_crypto_internal_basic_sig_ecdsa_secp256k1 as ecdsa_secp256k1; + use ic_crypto_internal_basic_sig_ecdsa_secp256r1 as ecdsa_secp256r1; + let rng = &mut reproducible_rng(); - let (env, inputs, _, _) = environment_with_sig_inputs(1..10, rng); - let combined_sig = run_tecdsa_protocol(&env, &inputs, rng); - let master_public_key = get_tecdsa_master_public_key(inputs.key_transcript()) - .expect("Master key extraction failed"); - let canister_public_key = - derive_tecdsa_public_key(&master_public_key, inputs.derivation_path()) - .expect("Public key derivation failed"); - - let ecdsa_sig = ecdsa_secp256k1::types::SignatureBytes( - <[u8; 64]>::try_from(combined_sig.signature).expect("Expected 64 bytes"), - ); - let ecdsa_pk = ecdsa_secp256k1::types::PublicKeyBytes(canister_public_key.public_key); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, inputs, _, _) = environment_with_sig_inputs(1..10, alg, rng); + let combined_sig = run_tecdsa_protocol(&env, &inputs, rng); + let master_public_key = get_tecdsa_master_public_key(inputs.key_transcript()) + .expect("Master key extraction failed"); + let canister_public_key = + derive_tecdsa_public_key(&master_public_key, inputs.derivation_path()) + .expect("Public key derivation failed"); + + match alg { + AlgorithmId::ThresholdEcdsaSecp256k1 => { + let ecdsa_sig = ecdsa_secp256k1::types::SignatureBytes( + <[u8; 64]>::try_from(combined_sig.signature).expect("Expected 64 bytes"), + ); + let ecdsa_pk = + ecdsa_secp256k1::types::PublicKeyBytes(canister_public_key.public_key); + + assert_eq!( + ecdsa_secp256k1::api::verify( + &ecdsa_sig, + inputs.hashed_message(), + &ecdsa_pk + ), + Ok(()), + "ECDSA sig verification failed" + ); + } + AlgorithmId::ThresholdEcdsaSecp256r1 => { + let ecdsa_sig = ecdsa_secp256r1::types::SignatureBytes( + <[u8; 64]>::try_from(combined_sig.signature).expect("Expected 64 bytes"), + ); + let ecdsa_pk = + ecdsa_secp256r1::types::PublicKeyBytes(canister_public_key.public_key); - assert_eq!( - ecdsa_secp256k1::api::verify(&ecdsa_sig, inputs.hashed_message(), &ecdsa_pk), - Ok(()), - "ECDSA sig verification failed" - ); + assert_eq!( + ecdsa_secp256r1::verify(&ecdsa_sig, inputs.hashed_message(), &ecdsa_pk), + Ok(()), + "ECDSA sig verification failed" + ); + } + unexpected => { + panic!("Unhandled ECDSA algorithm {}", unexpected) + } + } + } } } @@ -2733,18 +2670,24 @@ mod get_tecdsa_master_public_key { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let result = get_tecdsa_master_public_key(&key_transcript); - assert_matches!(result, Ok(_)); - let master_public_key = result.expect("Master key extraction failed"); - assert_eq!(master_public_key.algorithm_id, AlgorithmId::EcdsaSecp256k1); - assert_eq!(master_public_key.public_key.len(), 33); // 1 byte header + 32 bytes of field element + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let result = get_tecdsa_master_public_key(&key_transcript); + assert_matches!(result, Ok(_)); + let master_public_key = result.expect("Master key extraction failed"); + + // 1 byte header + 32 bytes of field element + let (expected_length, expected_alg) = match alg { + AlgorithmId::ThresholdEcdsaSecp256r1 => (1 + 32, AlgorithmId::EcdsaP256), + AlgorithmId::ThresholdEcdsaSecp256k1 => (1 + 32, AlgorithmId::EcdsaSecp256k1), + unexpected => { + panic!("Unexpected ECDSA algorithm {}", unexpected); + } + }; + + assert_eq!(master_public_key.algorithm_id, expected_alg); + assert_eq!(master_public_key.public_key.len(), expected_length); + } } #[test] @@ -2755,31 +2698,27 @@ mod get_tecdsa_master_public_key { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let master_public_key = - get_tecdsa_master_public_key(&key_transcript).expect("Master key extraction failed"); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let master_public_key = get_tecdsa_master_public_key(&key_transcript) + .expect("Master key extraction failed"); - let derivation_path_1 = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(42), - derivation_path: vec![], - }; - let derivation_path_2 = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(42), - derivation_path: vec![], - }; + let derivation_path_1 = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(42), + derivation_path: vec![], + }; + let derivation_path_2 = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(42), + derivation_path: vec![], + }; - assert_eq!(derivation_path_1, derivation_path_2); - let derived_pk_1 = derive_tecdsa_public_key(&master_public_key, &derivation_path_1) - .expect("Public key derivation failed "); - let derived_pk_2 = derive_tecdsa_public_key(&master_public_key, &derivation_path_2) - .expect("Public key derivation failed "); - assert_eq!(derived_pk_1, derived_pk_2); + assert_eq!(derivation_path_1, derivation_path_2); + let derived_pk_1 = derive_tecdsa_public_key(&master_public_key, &derivation_path_1) + .expect("Public key derivation failed "); + let derived_pk_2 = derive_tecdsa_public_key(&master_public_key, &derivation_path_2) + .expect("Public key derivation failed "); + assert_eq!(derived_pk_1, derived_pk_2); + } } #[test] @@ -2790,58 +2729,54 @@ mod get_tecdsa_master_public_key { let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let master_public_key = - get_tecdsa_master_public_key(&key_transcript).expect("Master key extraction failed"); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let master_public_key = get_tecdsa_master_public_key(&key_transcript) + .expect("Master key extraction failed"); - let derivation_paths = [ - ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], - }, - ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(2), - derivation_path: vec![], - }, - ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(3), - derivation_path: vec![], - }, - ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(3), - derivation_path: vec![vec![1, 2, 3, 4]], - }, - ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(3), - derivation_path: vec![vec![1, 2, 3, 5]], - }, - ]; - let mut derived_keys = std::collections::HashSet::new(); - for derivation_path in &derivation_paths { - let derived_pk = derive_tecdsa_public_key(&master_public_key, derivation_path) - .unwrap_or_else(|_| { - panic!( - "Public key derivation failed for derivation path {:?}", - derivation_path - ) - }); - assert!( - derived_keys.insert(derived_pk), - "Duplicate derived key for derivation path {:?}", - derivation_path + let derivation_paths = [ + ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }, + ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(2), + derivation_path: vec![], + }, + ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(3), + derivation_path: vec![], + }, + ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(3), + derivation_path: vec![vec![1, 2, 3, 4]], + }, + ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(3), + derivation_path: vec![vec![1, 2, 3, 5]], + }, + ]; + let mut derived_keys = std::collections::HashSet::new(); + for derivation_path in &derivation_paths { + let derived_pk = derive_tecdsa_public_key(&master_public_key, derivation_path) + .unwrap_or_else(|_| { + panic!( + "Public key derivation failed for derivation path {:?}", + derivation_path + ) + }); + assert!( + derived_keys.insert(derived_pk), + "Duplicate derived key for derivation path {:?}", + derivation_path + ); + } + assert_eq!( + derived_keys.len(), + derivation_paths.len(), + "# of derived keys does not match # of derivation paths" ); } - assert_eq!( - derived_keys.len(), - derivation_paths.len(), - "# of derived keys does not match # of derivation paths" - ); } #[test] @@ -2851,24 +2786,21 @@ mod get_tecdsa_master_public_key { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let master_ecdsa_key = get_tecdsa_master_public_key(&key_transcript); - assert_matches!(master_ecdsa_key, Ok(_)); - let derivation_path = ExtendedDerivationPath { - caller: PrincipalId::new_user_test_id(1), - derivation_path: vec![], - }; - let derived_public_key = - derive_tecdsa_public_key(&master_ecdsa_key.unwrap(), &derivation_path); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let master_ecdsa_key = get_tecdsa_master_public_key(&key_transcript); + assert_matches!(master_ecdsa_key, Ok(_)); + let derivation_path = ExtendedDerivationPath { + caller: PrincipalId::new_user_test_id(1), + derivation_path: vec![], + }; + + let derived_public_key = + derive_tecdsa_public_key(&master_ecdsa_key.unwrap(), &derivation_path); - assert_matches!(derived_public_key, Ok(_)); + assert_matches!(derived_public_key, Ok(_)); + } } } @@ -2894,19 +2826,17 @@ mod verify_dealing_private { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let dealer = env.nodes.random_dealer(¶ms, rng); - let signed_dealing = dealer.create_dealing_or_panic(¶ms); - let receiver = env.nodes.random_receiver(params.receivers(), rng); - let result = receiver.verify_dealing_private(¶ms, &signed_dealing); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let dealer = env.nodes.random_dealer(¶ms, rng); + let signed_dealing = dealer.create_dealing_or_panic(¶ms); + let receiver = env.nodes.random_receiver(params.receivers(), rng); + + let result = receiver.verify_dealing_private(¶ms, &signed_dealing); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] @@ -2915,33 +2845,31 @@ mod verify_dealing_private { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let dealer = env.nodes.random_dealer(¶ms, rng); - let signed_dealing_with_corrupted_signature = dealer - .create_dealing_or_panic(¶ms) - .into_builder() - .corrupt_signature() - .build(); - let receiver = env.nodes.random_receiver(params.receivers(), rng); - - let (result_verify_public, result_verify_private) = verify_dealing_public_and_private( - receiver, - ¶ms, - &signed_dealing_with_corrupted_signature, - ); - assert_matches!( - (result_verify_public, result_verify_private), - ( - Err(IDkgVerifyDealingPublicError::InvalidSignature { .. }), - Ok(()) - ) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let dealer = env.nodes.random_dealer(¶ms, rng); + let signed_dealing_with_corrupted_signature = dealer + .create_dealing_or_panic(¶ms) + .into_builder() + .corrupt_signature() + .build(); + let receiver = env.nodes.random_receiver(params.receivers(), rng); + + let (result_verify_public, result_verify_private) = verify_dealing_public_and_private( + receiver, + ¶ms, + &signed_dealing_with_corrupted_signature, + ); + + assert_matches!( + (result_verify_public, result_verify_private), + ( + Err(IDkgVerifyDealingPublicError::InvalidSignature { .. }), + Ok(()) + ) + ); + } } #[test] @@ -2962,17 +2890,20 @@ mod verify_dealing_private { IDkgReceivers::new(receivers_ids).expect("valid receivers"), ) }; - let params = env.params_for_random_sharing( - &dealers_with_at_least_one_common_node, - &receivers_with_at_least_one_common_node, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let signed_dealing = dealer_and_receiver.create_dealing_or_panic(¶ms); - let result = dealer_and_receiver.verify_dealing_private(¶ms, &signed_dealing); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing( + &dealers_with_at_least_one_common_node, + &receivers_with_at_least_one_common_node, + alg, + rng, + ); + let signed_dealing = dealer_and_receiver.create_dealing_or_panic(¶ms); + + let result = dealer_and_receiver.verify_dealing_private(¶ms, &signed_dealing); - assert_eq!(result, Ok(())); + assert_eq!(result, Ok(())); + } } #[test] @@ -2981,25 +2912,23 @@ mod verify_dealing_private { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let dealer = env.nodes.random_dealer(¶ms, rng); - let signed_dealing = dealer.create_dealing_or_panic(¶ms); - let receiver = env.nodes.random_receiver(params.receivers(), rng); - let result = receiver.verify_dealing_private( - ¶ms, - &signed_dealing - .into_builder() - .corrupt_transcript_id() - .build_with_signature(¶ms, dealer, dealer.id()), - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let dealer = env.nodes.random_dealer(¶ms, rng); + let signed_dealing = dealer.create_dealing_or_panic(¶ms); + let receiver = env.nodes.random_receiver(params.receivers(), rng); + + let result = receiver.verify_dealing_private( + ¶ms, + &signed_dealing + .into_builder() + .corrupt_transcript_id() + .build_with_signature(¶ms, dealer, dealer.id()), + ); - assert_matches!( result, Err(IDkgVerifyDealingPrivateError::InvalidArgument(reason)) if reason.starts_with("mismatching transcript IDs")); + assert_matches!( result, Err(IDkgVerifyDealingPrivateError::InvalidArgument(reason)) if reason.starts_with("mismatching transcript IDs")); + } } #[test] @@ -3008,25 +2937,23 @@ mod verify_dealing_private { let subnet_size = rng.gen_range(1..10); let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let dealer = env.nodes.random_dealer(¶ms, rng); - let signed_dealing = dealer.create_dealing_or_panic(¶ms); - let receiver = env.nodes.random_receiver(params.receivers(), rng); - let result = receiver.verify_dealing_private( - ¶ms, - &signed_dealing - .into_builder() - .corrupt_internal_dealing_raw_by_flipping_bit() - .build_with_signature(¶ms, dealer, dealer.id()), - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); + let dealer = env.nodes.random_dealer(¶ms, rng); + let signed_dealing = dealer.create_dealing_or_panic(¶ms); + let receiver = env.nodes.random_receiver(params.receivers(), rng); + + let result = receiver.verify_dealing_private( + ¶ms, + &signed_dealing + .into_builder() + .corrupt_internal_dealing_raw_by_flipping_bit() + .build_with_signature(¶ms, dealer, dealer.id()), + ); - assert_matches!( result, Err(IDkgVerifyDealingPrivateError::InvalidArgument(reason)) if reason.starts_with("failed to deserialize internal dealing")); + assert_matches!( result, Err(IDkgVerifyDealingPrivateError::InvalidArgument(reason)) if reason.starts_with("failed to deserialize internal dealing")); + } } #[test] @@ -3035,13 +2962,16 @@ mod verify_dealing_private { let registry_client_error = RegistryClientError::PollLockFailed { error: "oh no!".to_string(), }; - let setup = Setup::new_with_registry_error(registry_client_error.clone(), rng); - assert_matches!( - setup.crypto.verify_dealing_private(&setup.params, &setup.signed_dealing), - Err(IDkgVerifyDealingPrivateError::RegistryError(error)) - if error == registry_client_error - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let setup = Setup::new_with_registry_error(alg, registry_client_error.clone(), rng); + + assert_matches!( + setup.crypto.verify_dealing_private(&setup.params, &setup.signed_dealing), + Err(IDkgVerifyDealingPrivateError::RegistryError(error)) + if error == registry_client_error + ); + } } #[test] @@ -3057,14 +2987,17 @@ mod verify_dealing_private { IDkgVerifyDealingPrivateError::InvalidDealing("invalid proof".to_string()), ]; let rng = &mut reproducible_rng(); - for csp_error in csp_errors { - let setup = Setup::new_with_expected_csp_error(csp_error.clone(), rng); - assert_matches!( + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + for csp_error in &csp_errors { + let setup = Setup::new_with_expected_csp_error(alg, csp_error.clone(), rng); + + assert_matches!( setup.crypto.verify_dealing_private(&setup.params, &setup.signed_dealing), Err(error) - if error == csp_error - ); + if error == *csp_error + ); + } } } @@ -3095,10 +3028,12 @@ mod verify_dealing_private { impl Setup { fn new_with_registry_error( + alg: AlgorithmId, expected_registry_error: RegistryClientError, rng: &mut ReproducibleRng, ) -> Setup { Self::new_with_csp_and_optional_registry_client_error( + alg, MockAllCryptoServiceProvider::new(), Some(expected_registry_error), rng, @@ -3106,6 +3041,7 @@ mod verify_dealing_private { } fn new_with_expected_csp_error( + alg: AlgorithmId, expected_csp_error: IDkgVerifyDealingPrivateError, rng: &mut ReproducibleRng, ) -> Setup { @@ -3114,10 +3050,11 @@ mod verify_dealing_private { .times(1) .returning(move |_, _, _, _, _, _| Err(expected_csp_error.clone())); - Self::new_with_csp_and_optional_registry_client_error(csp, None, rng) + Self::new_with_csp_and_optional_registry_client_error(alg, csp, None, rng) } fn new_with_csp_and_optional_registry_client_error( + alg: AlgorithmId, csp: MockAllCryptoServiceProvider, registry_client_error: Option, rng: &mut ReproducibleRng, @@ -3126,12 +3063,7 @@ mod verify_dealing_private { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); let dealer = env.nodes.random_dealer(¶ms, rng); let signed_dealing = dealer.create_dealing_or_panic(¶ms); let node_id = *receivers @@ -3199,25 +3131,23 @@ mod verify_dealing_public { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, &mut rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let dealer = env.nodes.random_dealer(¶ms, &mut rng); - let signed_dealing = dealer.create_dealing_or_panic(¶ms); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, &mut rng); + let dealer = env.nodes.random_dealer(¶ms, &mut rng); + + let signed_dealing = dealer.create_dealing_or_panic(¶ms); - let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - let verifier = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(verifier_id) - .with_rng(rng) - .build(); + let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + let verifier = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(verifier_id) + .with_rng(rng.fork()) + .build(); - let result = verifier.verify_dealing_public(¶ms, &signed_dealing); - assert_eq!(result, Ok(())); + let result = verifier.verify_dealing_public(¶ms, &signed_dealing); + assert_eq!(result, Ok(())); + } } #[test] @@ -3227,32 +3157,30 @@ mod verify_dealing_public { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, &mut rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let dealer = env.nodes.random_dealer(¶ms, &mut rng); - let signed_dealing = dealer - .create_dealing_or_panic(¶ms) - .into_builder() - .corrupt_signature() - .build(); - - let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - let verifier = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(verifier_id) - .with_rng(rng) - .build(); - - let result = verifier.verify_dealing_public(¶ms, &signed_dealing); - - assert_matches!(result, - Err(IDkgVerifyDealingPublicError::InvalidSignature { error, .. }) - if error.contains("Invalid basic signature on signed iDKG dealing from signer") - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, &mut rng); + let dealer = env.nodes.random_dealer(¶ms, &mut rng); + let signed_dealing = dealer + .create_dealing_or_panic(¶ms) + .into_builder() + .corrupt_signature() + .build(); + + let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + let verifier = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(verifier_id) + .with_rng(rng.fork()) + .build(); + + let result = verifier.verify_dealing_public(¶ms, &signed_dealing); + + assert_matches!(result, + Err(IDkgVerifyDealingPublicError::InvalidSignature { error, .. }) + if error.contains("Invalid basic signature on signed iDKG dealing from signer") + ); + } } #[test] @@ -3262,32 +3190,30 @@ mod verify_dealing_public { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, &mut rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let dealer = env.nodes.random_dealer(¶ms, &mut rng); - let signed_dealing = dealer - .create_dealing_or_panic(¶ms) - .into_builder() - .corrupt_transcript_id() - .build_with_signature(¶ms, dealer, dealer.id()); - - let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - let verifier = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(verifier_id) - .with_rng(rng) - .build(); - - let result = verifier.verify_dealing_public(¶ms, &signed_dealing); - - assert_matches!( - result, - Err(IDkgVerifyDealingPublicError::TranscriptIdMismatch) - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, &mut rng); + let dealer = env.nodes.random_dealer(¶ms, &mut rng); + let signed_dealing = dealer + .create_dealing_or_panic(¶ms) + .into_builder() + .corrupt_transcript_id() + .build_with_signature(¶ms, dealer, dealer.id()); + + let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + let verifier = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(verifier_id) + .with_rng(rng.fork()) + .build(); + + let result = verifier.verify_dealing_public(¶ms, &signed_dealing); + + assert_matches!( + result, + Err(IDkgVerifyDealingPublicError::TranscriptIdMismatch) + ); + } } #[test] @@ -3303,37 +3229,35 @@ mod verify_dealing_public { }, &mut rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let dealer = env.nodes.random_dealer(¶ms, &mut rng); - let other_dealer = env - .nodes - .dealers(¶ms) - .find(|node| *node != dealer) - .expect("not enough nodes"); - let signed_dealing = dealer - .create_dealing_or_panic(¶ms) - .into_builder() - .with_dealer_id(other_dealer.id()) - .build_with_signature(¶ms, other_dealer, other_dealer.id()); - - let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - let verifier = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(verifier_id) - .with_rng(rng) - .build(); - - let result = verifier.verify_dealing_public(¶ms, &signed_dealing); - - assert_matches!( - result, - Err(IDkgVerifyDealingPublicError::InvalidDealing {reason}) if reason == "InvalidProof" - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, &mut rng); + let dealer = env.nodes.random_dealer(¶ms, &mut rng); + let other_dealer = env + .nodes + .dealers(¶ms) + .find(|node| *node != dealer) + .expect("not enough nodes"); + let signed_dealing = dealer + .create_dealing_or_panic(¶ms) + .into_builder() + .with_dealer_id(other_dealer.id()) + .build_with_signature(¶ms, other_dealer, other_dealer.id()); + + let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + let verifier = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(verifier_id) + .with_rng(rng.fork()) + .build(); + + let result = verifier.verify_dealing_public(¶ms, &signed_dealing); + + assert_matches!( + result, + Err(IDkgVerifyDealingPublicError::InvalidDealing {reason}) if reason == "InvalidProof" + ); + } } #[test] @@ -3343,44 +3267,42 @@ mod verify_dealing_public { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, &mut rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let dealer = env.nodes.random_dealer(¶ms, &mut rng); - // We need the signature verification to succeed, so the public key of the valid dealer in - // the registry needs to be copied to a non-dealer. The subsequent dealer index check will - // fail (which is what we are testing), since the `NodeId` of the non-dealer is not - // included in the list of dealers in params. - let not_a_dealer_node_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - copy_node_signing_key_in_registry_from_one_node_to_another( - &env, - dealer.id(), - not_a_dealer_node_id, - ); - let signed_dealing = dealer - .create_dealing_or_panic(¶ms) - .into_builder() - .build_with_signature(¶ms, dealer, dealer.id()) - .into_builder() - .with_dealer_id(not_a_dealer_node_id) - .build(); - - let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - let verifier = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(verifier_id) - .with_rng(rng) - .build(); - - let result = verifier.verify_dealing_public(¶ms, &signed_dealing); - - assert_matches!( - result, - Err(IDkgVerifyDealingPublicError::InvalidDealing {reason}) if reason == "No such dealer" - ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, &mut rng); + let dealer = env.nodes.random_dealer(¶ms, &mut rng); + // We need the signature verification to succeed, so the public key of the valid dealer in + // the registry needs to be copied to a non-dealer. The subsequent dealer index check will + // fail (which is what we are testing), since the `NodeId` of the non-dealer is not + // included in the list of dealers in params. + let not_a_dealer_node_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + copy_node_signing_key_in_registry_from_one_node_to_another( + &env, + dealer.id(), + not_a_dealer_node_id, + ); + let signed_dealing = dealer + .create_dealing_or_panic(¶ms) + .into_builder() + .build_with_signature(¶ms, dealer, dealer.id()) + .into_builder() + .with_dealer_id(not_a_dealer_node_id) + .build(); + + let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + let verifier = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(verifier_id) + .with_rng(rng.fork()) + .build(); + + let result = verifier.verify_dealing_public(¶ms, &signed_dealing); + + assert_matches!( + result, + Err(IDkgVerifyDealingPublicError::InvalidDealing {reason}) if reason == "No such dealer" + ); + } } #[test] @@ -3390,32 +3312,29 @@ mod verify_dealing_public { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, &mut rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::Random, &mut rng); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &mut rng, - ); - let dealer = env.nodes.random_dealer(¶ms, &mut rng); - let signed_dealing = dealer - .create_dealing_or_panic(¶ms) - .into_builder() - .corrupt_internal_dealing_raw_by_flipping_bit() - .build_with_signature(¶ms, dealer, dealer.id()); - - let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); - let verifier = TempCryptoComponent::builder() - .with_registry(Arc::clone(&env.registry) as Arc<_>) - .with_node_id(verifier_id) - .with_rng(rng) - .build(); - - let result = verifier.verify_dealing_public(¶ms, &signed_dealing); - - assert_matches!( - result, - Err(IDkgVerifyDealingPublicError::InvalidDealing {reason}) if reason.starts_with("ThresholdEcdsaSerializationError") - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let params = env.params_for_random_sharing(&dealers, &receivers, alg, &mut rng); + let dealer = env.nodes.random_dealer(¶ms, &mut rng); + let signed_dealing = dealer + .create_dealing_or_panic(¶ms) + .into_builder() + .corrupt_internal_dealing_raw_by_flipping_bit() + .build_with_signature(¶ms, dealer, dealer.id()); + + let verifier_id = random_node_id_excluding(&env.nodes.ids(), &mut rng); + let verifier = TempCryptoComponent::builder() + .with_registry(Arc::clone(&env.registry) as Arc<_>) + .with_node_id(verifier_id) + .with_rng(rng.fork()) + .build(); + + let result = verifier.verify_dealing_public(¶ms, &signed_dealing); + + assert_matches!( + result, + Err(IDkgVerifyDealingPublicError::InvalidDealing {reason}) if reason.starts_with("ThresholdEcdsaSerializationError") + ); + } } fn copy_node_signing_key_in_registry_from_one_node_to_another( @@ -3462,28 +3381,32 @@ mod verify_initial_dealings { num_destination_subnet >= 1, "number of nodes in destination subnet is less than 1" ); - let env = CanisterThresholdSigTestEnvironment::new(num_nodes, rng); - let external_verifier = Node::new( - random_node_id_excluding(&env.nodes.ids(), rng), - Arc::clone(&env.registry), - rng, - ); - let (source_subnet_nodes, destination_subnet_nodes) = env - .nodes - .partition(|(index, _node)| *index < num_source_subnet); - let (initial_dealings, reshare_of_unmasked_params) = generate_initial_dealings( - env.newest_registry_version, - source_subnet_nodes, - destination_subnet_nodes, - false, - rng, - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(num_nodes, rng); + let external_verifier = Node::new( + random_node_id_excluding(&env.nodes.ids(), rng), + Arc::clone(&env.registry), + rng, + ); + let (source_subnet_nodes, destination_subnet_nodes) = env + .nodes + .partition(|(index, _node)| *index < num_source_subnet); - assert_eq!( - external_verifier - .verify_initial_dealings(&reshare_of_unmasked_params, &initial_dealings), - Ok(()) - ); + let (initial_dealings, reshare_of_unmasked_params) = generate_initial_dealings( + alg, + env.newest_registry_version, + source_subnet_nodes, + destination_subnet_nodes, + false, + rng, + ); + + assert_eq!( + external_verifier + .verify_initial_dealings(&reshare_of_unmasked_params, &initial_dealings), + Ok(()) + ); + } } #[test] @@ -3494,38 +3417,43 @@ mod verify_initial_dealings { let num_destination_subnet = num_nodes - num_source_subnet; assert!( num_destination_subnet >= 1, - "number of nodes in destination subnet is less than 1" - ); - let env = CanisterThresholdSigTestEnvironment::new(num_nodes, rng); - let verifier = Node::new( - random_node_id_excluding(&env.nodes.ids(), rng), - Arc::clone(&env.registry), - rng, - ); - let (source_subnet_nodes, destination_subnet_nodes) = env - .nodes - .partition(|(index, _node)| *index < num_source_subnet); - let (initial_dealings, reshare_of_unmasked_params) = generate_initial_dealings( - env.newest_registry_version, - source_subnet_nodes, - destination_subnet_nodes, - false, - rng, - ); - let other_params = IDkgTranscriptParams::new( - random_transcript_id(rng), - reshare_of_unmasked_params.dealers().get().clone(), - reshare_of_unmasked_params.receivers().get().clone(), - env.newest_registry_version, - AlgorithmId::ThresholdEcdsaSecp256k1, - IDkgTranscriptOperation::Random, - ) - .expect("failed to create random IDkgTranscriptParams"); - - assert_matches!( - verifier.verify_initial_dealings(&other_params, &initial_dealings), - Err(IDkgVerifyInitialDealingsError::MismatchingTranscriptParams) + "number of nodes in destination subnet is less than 1" ); + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(num_nodes, rng); + let verifier = Node::new( + random_node_id_excluding(&env.nodes.ids(), rng), + Arc::clone(&env.registry), + rng, + ); + let (source_subnet_nodes, destination_subnet_nodes) = env + .nodes + .partition(|(index, _node)| *index < num_source_subnet); + let (initial_dealings, reshare_of_unmasked_params) = generate_initial_dealings( + alg, + env.newest_registry_version, + source_subnet_nodes, + destination_subnet_nodes, + false, + rng, + ); + + let other_params = IDkgTranscriptParams::new( + random_transcript_id(rng), + reshare_of_unmasked_params.dealers().get().clone(), + reshare_of_unmasked_params.receivers().get().clone(), + env.newest_registry_version, + alg, + IDkgTranscriptOperation::Random, + ) + .expect("failed to create random IDkgTranscriptParams"); + + assert_matches!( + verifier.verify_initial_dealings(&other_params, &initial_dealings), + Err(IDkgVerifyInitialDealingsError::MismatchingTranscriptParams) + ); + } } #[test] @@ -3538,34 +3466,39 @@ mod verify_initial_dealings { num_destination_subnet >= 1, "number of nodes in destination subnet is less than 1" ); - let env = CanisterThresholdSigTestEnvironment::new(num_nodes, rng); - let verifier = Node::new( - random_node_id_excluding(&env.nodes.ids(), rng), - Arc::clone(&env.registry), - rng, - ); - let (source_subnet_nodes, destination_subnet_nodes) = env - .nodes - .partition(|(index, _node)| *index < num_source_subnet); - let (initial_dealings_with_first_corrupted, reshare_of_unmasked_params) = - generate_initial_dealings( - env.newest_registry_version, - source_subnet_nodes, - destination_subnet_nodes, - true, + + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(num_nodes, rng); + let verifier = Node::new( + random_node_id_excluding(&env.nodes.ids(), rng), + Arc::clone(&env.registry), rng, ); + let (source_subnet_nodes, destination_subnet_nodes) = env + .nodes + .partition(|(index, _node)| *index < num_source_subnet); + let (initial_dealings_with_first_corrupted, reshare_of_unmasked_params) = + generate_initial_dealings( + alg, + env.newest_registry_version, + source_subnet_nodes, + destination_subnet_nodes, + true, + rng, + ); - let result = verifier.verify_initial_dealings( - &reshare_of_unmasked_params, - &initial_dealings_with_first_corrupted, - ); - assert_matches!(result, Err(IDkgVerifyInitialDealingsError::PublicVerificationFailure { verify_dealing_public_error, ..}) - if matches!(verify_dealing_public_error, IDkgVerifyDealingPublicError::InvalidSignature { .. }) - ); + let result = verifier.verify_initial_dealings( + &reshare_of_unmasked_params, + &initial_dealings_with_first_corrupted, + ); + assert_matches!(result, Err(IDkgVerifyInitialDealingsError::PublicVerificationFailure { verify_dealing_public_error, ..}) + if matches!(verify_dealing_public_error, IDkgVerifyDealingPublicError::InvalidSignature { .. }) + ); + } } fn generate_initial_dealings( + alg: AlgorithmId, registry_version: RegistryVersion, source_subnet_nodes: Nodes, target_subnet_nodes: Nodes, @@ -3580,7 +3513,7 @@ mod verify_initial_dealings { source_dealers.get().clone(), source_receivers.get().clone(), registry_version, - AlgorithmId::ThresholdEcdsaSecp256k1, + alg, IDkgTranscriptOperation::Random, ) .expect("failed to create random IDkgTranscriptParams"); @@ -3637,95 +3570,101 @@ mod open_transcript { #[test] fn should_open_transcript_successfully() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); - let result = opener.open_transcript(&transcript, complainer.id(), &complaint); - assert_matches!(result, Ok(_)); + let result = opener.open_transcript(&transcript, complainer.id(), &complaint); + assert_matches!(result, Ok(_)); + } } #[test] fn should_fail_open_transcript_with_invalid_share() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = complainer; // opener's share is invalid - let result = opener.open_transcript(&transcript, opener.id(), &complaint); - assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) - if internal_error.contains("InvalidCommitment")); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = complainer; // opener's share is invalid + let result = opener.open_transcript(&transcript, opener.id(), &complaint); + assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) + if internal_error.contains("InvalidCommitment")); + } } #[test] fn should_fail_open_transcript_when_missing_a_dealing() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - // Remove the corrupted dealing from the transcript. - transcript.verified_dealings.remove( - &transcript - .index_for_dealer_id(complaint.dealer_id) - .expect("Missing dealer of corrupted dealing"), - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + // Remove the corrupted dealing from the transcript. + transcript.verified_dealings.remove( + &transcript + .index_for_dealer_id(complaint.dealer_id) + .expect("Missing dealer of corrupted dealing"), + ); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let result = opener.open_transcript(&transcript, complainer.id(), &complaint); - assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) - if internal_error.contains("MissingDealing")); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + let result = opener.open_transcript(&transcript, complainer.id(), &complaint); + assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) + if internal_error.contains("MissingDealing")); + } } #[test] fn should_fail_open_transcript_with_an_invalid_complaint() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, mut complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - // Set "wrong" dealer_id in the complaint - complaint.dealer_id = random_dealer_id_excluding(&transcript, complaint.dealer_id, rng); - - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let result = opener.open_transcript(&transcript, complainer.id(), &complaint); - assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) - if internal_error.contains("InvalidComplaint")); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, mut complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + // Set "wrong" dealer_id in the complaint + complaint.dealer_id = random_dealer_id_excluding(&transcript, complaint.dealer_id, rng); + + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + let result = opener.open_transcript(&transcript, complainer.id(), &complaint); + assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) + if internal_error.contains("InvalidComplaint")); + } } #[test] fn should_fail_open_transcript_with_a_valid_complaint_but_wrong_transcript() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - - // Create another environment of the same size, and generate a transcript for it. - let env_2 = CanisterThresholdSigTestEnvironment::new(env.nodes.len(), rng); - let (dealers_2, receivers_2) = - env_2.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); - let params_2 = env_2.params_for_random_sharing( - &dealers_2, - &receivers_2, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let transcript_2 = &env_2 - .nodes - .run_idkg_and_create_and_verify_transcript(¶ms_2, rng); - // Try `open_transcript` but with a wrong transcript. - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let result = opener.open_transcript(transcript_2, complainer.id(), &complaint); - assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) - if internal_error.contains("InvalidArgumentMismatchingTranscriptIDs")); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + + // Create another environment of the same size, and generate a transcript for it. + let env_2 = CanisterThresholdSigTestEnvironment::new(env.nodes.len(), rng); + let (dealers_2, receivers_2) = + env_2.choose_dealers_and_receivers(&IDkgParticipants::Random, rng); + let params_2 = env_2.params_for_random_sharing(&dealers_2, &receivers_2, alg, rng); + let transcript_2 = &env_2 + .nodes + .run_idkg_and_create_and_verify_transcript(¶ms_2, rng); + + // Try `open_transcript` but with a wrong transcript. + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + let result = opener.open_transcript(transcript_2, complainer.id(), &complaint); + assert_matches!(result, Err(IDkgOpenTranscriptError::InternalError { internal_error }) + if internal_error.contains("InvalidArgumentMismatchingTranscriptIDs")); + } } } @@ -3735,165 +3674,186 @@ mod verify_opening { #[test] fn should_verify_opening_successfully() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); - assert_eq!(result, Ok(())); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); + assert_eq!(result, Ok(())); + } } #[test] fn should_fail_verify_opening_with_inconsistent_transcript_id_in_opening() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - - let mut opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - let wrong_transcript_id = dummy_idkg_transcript_id_for_tests(1); - assert_ne!( - opening.transcript_id, wrong_transcript_id, - "Unexpected collision with a random transcript_id" - ); - opening.transcript_id = wrong_transcript_id; - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); - assert_matches!(result, Err(IDkgVerifyOpeningError::TranscriptIdMismatch)); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let mut opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + let wrong_transcript_id = dummy_idkg_transcript_id_for_tests(1); + assert_ne!( + opening.transcript_id, wrong_transcript_id, + "Unexpected collision with a random transcript_id" + ); + opening.transcript_id = wrong_transcript_id; + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); + assert_matches!(result, Err(IDkgVerifyOpeningError::TranscriptIdMismatch)); + } } #[test] fn should_fail_verify_opening_with_inconsistent_transcript_id_in_complaint() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, mut complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - let wrong_transcript_id = dummy_idkg_transcript_id_for_tests(1); - assert_ne!( - complaint.transcript_id, wrong_transcript_id, - "Unexpected collision with a random transcript_id" - ); - complaint.transcript_id = wrong_transcript_id; - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, mut complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + let wrong_transcript_id = dummy_idkg_transcript_id_for_tests(1); + assert_ne!( + complaint.transcript_id, wrong_transcript_id, + "Unexpected collision with a random transcript_id" + ); + complaint.transcript_id = wrong_transcript_id; + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); - assert_matches!(result, Err(IDkgVerifyOpeningError::TranscriptIdMismatch)); + let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); + assert_matches!(result, Err(IDkgVerifyOpeningError::TranscriptIdMismatch)); + } } #[test] fn should_fail_verify_opening_with_inconsistent_dealer_id() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let mut opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - opening.dealer_id = random_dealer_id_excluding(&transcript, opening.dealer_id, rng); - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let mut opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + opening.dealer_id = random_dealer_id_excluding(&transcript, opening.dealer_id, rng); + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); - assert_matches!(result, Err(IDkgVerifyOpeningError::DealerIdMismatch)); + let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); + assert_matches!(result, Err(IDkgVerifyOpeningError::DealerIdMismatch)); + } } #[test] fn should_fail_verify_opening_when_opener_is_not_a_receiver() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let wrong_opener_id = node_id(123456789); - assert!( - !transcript.receivers.get().contains(&wrong_opener_id), - "Wrong opener_id unexpectedly in receivers" - ); - let result = verifier.verify_opening(&transcript, wrong_opener_id, &opening, &complaint); - assert_matches!( - result, - Err(IDkgVerifyOpeningError::MissingOpenerInReceivers { .. }) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + let wrong_opener_id = node_id(123456789); + assert!( + !transcript.receivers.get().contains(&wrong_opener_id), + "Wrong opener_id unexpectedly in receivers" + ); + let result = + verifier.verify_opening(&transcript, wrong_opener_id, &opening, &complaint); + assert_matches!( + result, + Err(IDkgVerifyOpeningError::MissingOpenerInReceivers { .. }) + ); + } } #[test] fn should_fail_verify_opening_with_corrupted_opening() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - - let mut opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - opening - .internal_opening_raw - .truncate(opening.internal_opening_raw.len() - 1); - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); - assert_matches!(result, Err(IDkgVerifyOpeningError::InternalError { .. })); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let mut opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + opening + .internal_opening_raw + .truncate(opening.internal_opening_raw.len() - 1); + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + + let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); + assert_matches!(result, Err(IDkgVerifyOpeningError::InternalError { .. })); + } } #[test] fn should_fail_verify_opening_when_dealing_is_missing() { let rng = &mut reproducible_rng(); - let (env, params, mut transcript) = environment_and_transcript_for_complaint(rng); - let (complainer, complaint) = - generate_single_complaint(&mut transcript, ¶ms, &env, rng); - let opener = env - .nodes - .random_receiver_excluding(complainer, &transcript.receivers, rng); - let opening = opener - .open_transcript(&transcript, complainer.id(), &complaint) - .expect("Unexpected failure of open_transcript"); - let verifier = env.nodes.random_receiver(&transcript.receivers, rng); - let dealings = transcript.verified_dealings.clone(); - let (dealer_index, _signed_dealing) = dealings - .iter() - .find(|(_index, batch_signed_dealing)| { - batch_signed_dealing.dealer_id() == complaint.dealer_id - }) - .expect("Inconsistent transcript"); - transcript.verified_dealings.remove(dealer_index); - let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); - assert_matches!( - result, - Err(IDkgVerifyOpeningError::MissingDealingInTranscript { .. }) - ); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let (env, params, mut transcript) = environment_and_transcript_for_complaint(alg, rng); + let (complainer, complaint) = + generate_single_complaint(&mut transcript, ¶ms, &env, rng); + let opener = + env.nodes + .random_receiver_excluding(complainer, &transcript.receivers, rng); + + let opening = opener + .open_transcript(&transcript, complainer.id(), &complaint) + .expect("Unexpected failure of open_transcript"); + let verifier = env.nodes.random_receiver(&transcript.receivers, rng); + let dealings = transcript.verified_dealings.clone(); + let (dealer_index, _signed_dealing) = dealings + .iter() + .find(|(_index, batch_signed_dealing)| { + batch_signed_dealing.dealer_id() == complaint.dealer_id + }) + .expect("Inconsistent transcript"); + transcript.verified_dealings.remove(dealer_index); + let result = verifier.verify_opening(&transcript, opener.id(), &opening, &complaint); + assert_matches!( + result, + Err(IDkgVerifyOpeningError::MissingDealingInTranscript { .. }) + ); + } } } @@ -3911,86 +3871,90 @@ mod reshare_key_transcript { .map(|n| n * 2) .choose(rng) .expect("non-empty iterator"); - let env = CanisterThresholdSigTestEnvironment::new(even_subnet_size, rng); - let (source_subnet_nodes, target_subnet_nodes) = env - .nodes - .partition(|(index, _node)| *index < even_subnet_size / 2); - assert_eq!(source_subnet_nodes.len(), target_subnet_nodes.len()); - let (source_dealers, source_receivers) = source_subnet_nodes - .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let source_key_transcript = { - let masked_key_params = IDkgTranscriptParams::new( + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(even_subnet_size, rng); + let (source_subnet_nodes, target_subnet_nodes) = env + .nodes + .partition(|(index, _node)| *index < even_subnet_size / 2); + assert_eq!(source_subnet_nodes.len(), target_subnet_nodes.len()); + let (source_dealers, source_receivers) = source_subnet_nodes + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); + let source_key_transcript = { + let masked_key_params = IDkgTranscriptParams::new( + random_transcript_id(rng), + source_dealers.get().clone(), + source_receivers.get().clone(), + env.newest_registry_version, + alg, + IDkgTranscriptOperation::Random, + ) + .expect("failed to create random IDkgTranscriptParams"); + let masked_key_transcript = source_subnet_nodes + .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); + let unmasked_params = build_params_from_previous( + masked_key_params, + IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript), + rng, + ); + source_subnet_nodes.run_idkg_and_create_and_verify_transcript(&unmasked_params, rng) + }; + let source_tecdsa_master_public_key = + get_tecdsa_master_public_key(&source_key_transcript).expect("valid public key"); + + let reshare_params = IDkgTranscriptParams::new( random_transcript_id(rng), - source_dealers.get().clone(), source_receivers.get().clone(), - env.newest_registry_version, - AlgorithmId::ThresholdEcdsaSecp256k1, - IDkgTranscriptOperation::Random, + target_subnet_nodes.ids(), + source_key_transcript.registry_version, + source_key_transcript.algorithm_id, + IDkgTranscriptOperation::ReshareOfUnmasked(source_key_transcript), ) - .expect("failed to create random IDkgTranscriptParams"); - let masked_key_transcript = source_subnet_nodes - .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); - let unmasked_params = build_params_from_previous( - masked_key_params, - IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript), - rng, - ); - source_subnet_nodes.run_idkg_and_create_and_verify_transcript(&unmasked_params, rng) - }; - let source_tecdsa_master_public_key = - get_tecdsa_master_public_key(&source_key_transcript).expect("valid public key"); - - let reshare_params = IDkgTranscriptParams::new( - random_transcript_id(rng), - source_receivers.get().clone(), - target_subnet_nodes.ids(), - source_key_transcript.registry_version, - source_key_transcript.algorithm_id, - IDkgTranscriptOperation::ReshareOfUnmasked(source_key_transcript), - ) - .expect("invalid reshare of unmasked parameters"); + .expect("invalid reshare of unmasked parameters"); - let nodes_involved_in_resharing: Nodes = source_subnet_nodes - .into_receivers(&source_receivers) - .chain(target_subnet_nodes.into_iter()) - .collect(); - let initial_dealings = { - let signed_dealings = nodes_involved_in_resharing - .load_previous_transcripts_and_create_signed_dealings(&reshare_params); - let initial_dealings = InitialIDkgDealings::new( - reshare_params.clone(), - signed_dealings.into_values().collect::>(), - ) - .expect("should create initial dealings"); - assert_eq!( + let nodes_involved_in_resharing: Nodes = source_subnet_nodes + .into_receivers(&source_receivers) + .chain(target_subnet_nodes.into_iter()) + .collect(); + let initial_dealings = { + let signed_dealings = nodes_involved_in_resharing + .load_previous_transcripts_and_create_signed_dealings(&reshare_params); + let initial_dealings = InitialIDkgDealings::new( + reshare_params.clone(), + signed_dealings.into_values().collect::>(), + ) + .expect("should create initial dealings"); + assert_eq!( + nodes_involved_in_resharing + .random_receiver(&reshare_params, rng) + .verify_initial_dealings(&reshare_params, &initial_dealings), + Ok(()) + ); + initial_dealings + }; + let reshared_key_transcript = { + let dealings = initial_dealings + .dealings() + .iter() + .map(|signed_dealing| { + nodes_involved_in_resharing.support_dealing_from_all_receivers( + signed_dealing.clone(), + &reshare_params, + ) + }) + .collect(); nodes_involved_in_resharing .random_receiver(&reshare_params, rng) - .verify_initial_dealings(&reshare_params, &initial_dealings), - Ok(()) - ); - initial_dealings - }; - let reshared_key_transcript = { - let dealings = initial_dealings - .dealings() - .iter() - .map(|signed_dealing| { - nodes_involved_in_resharing - .support_dealing_from_all_receivers(signed_dealing.clone(), &reshare_params) - }) - .collect(); - nodes_involved_in_resharing - .random_receiver(&reshare_params, rng) - .create_transcript_or_panic(&reshare_params, &dealings) - }; - let target_tecdsa_master_public_key = - get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); + .create_transcript_or_panic(&reshare_params, &dealings) + }; + let target_tecdsa_master_public_key = + get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); - assert_eq!( - source_tecdsa_master_public_key, - target_tecdsa_master_public_key - ); + assert_eq!( + source_tecdsa_master_public_key, + target_tecdsa_master_public_key + ); + } } #[test] @@ -4000,59 +3964,61 @@ mod reshare_key_transcript { .map(|n| n * 2) .choose(rng) .expect("non-empty iterator"); - let env = CanisterThresholdSigTestEnvironment::new(even_subnet_size, rng); - let (source_subnet_nodes, target_subnet_nodes) = env - .nodes - .partition(|(index, _node)| *index < even_subnet_size / 2); - assert_eq!(source_subnet_nodes.len(), target_subnet_nodes.len()); - let (source_dealers, source_receivers) = source_subnet_nodes - .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let source_key_transcript = { - let masked_key_params = IDkgTranscriptParams::new( + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let env = CanisterThresholdSigTestEnvironment::new(even_subnet_size, rng); + let (source_subnet_nodes, target_subnet_nodes) = env + .nodes + .partition(|(index, _node)| *index < even_subnet_size / 2); + assert_eq!(source_subnet_nodes.len(), target_subnet_nodes.len()); + let (source_dealers, source_receivers) = source_subnet_nodes + .choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); + let source_key_transcript = { + let masked_key_params = IDkgTranscriptParams::new( + random_transcript_id(rng), + source_dealers.get().clone(), + source_receivers.get().clone(), + env.newest_registry_version, + alg, + IDkgTranscriptOperation::Random, + ) + .expect("failed to create random IDkgTranscriptParams"); + let masked_key_transcript = source_subnet_nodes + .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); + let unmasked_params = build_params_from_previous( + masked_key_params, + IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript), + rng, + ); + source_subnet_nodes.run_idkg_and_create_and_verify_transcript(&unmasked_params, rng) + }; + let source_tecdsa_master_public_key = + get_tecdsa_master_public_key(&source_key_transcript).expect("valid public key"); + + let reshare_params = IDkgTranscriptParams::new( random_transcript_id(rng), - source_dealers.get().clone(), source_receivers.get().clone(), - env.newest_registry_version, - AlgorithmId::ThresholdEcdsaSecp256k1, - IDkgTranscriptOperation::Random, + target_subnet_nodes.ids(), + source_key_transcript.registry_version, + source_key_transcript.algorithm_id, + IDkgTranscriptOperation::ReshareOfUnmasked(source_key_transcript), ) - .expect("failed to create random IDkgTranscriptParams"); - let masked_key_transcript = source_subnet_nodes - .run_idkg_and_create_and_verify_transcript(&masked_key_params, rng); - let unmasked_params = build_params_from_previous( - masked_key_params, - IDkgTranscriptOperation::ReshareOfMasked(masked_key_transcript), - rng, - ); - source_subnet_nodes.run_idkg_and_create_and_verify_transcript(&unmasked_params, rng) - }; - let source_tecdsa_master_public_key = - get_tecdsa_master_public_key(&source_key_transcript).expect("valid public key"); - - let reshare_params = IDkgTranscriptParams::new( - random_transcript_id(rng), - source_receivers.get().clone(), - target_subnet_nodes.ids(), - source_key_transcript.registry_version, - source_key_transcript.algorithm_id, - IDkgTranscriptOperation::ReshareOfUnmasked(source_key_transcript), - ) - .expect("invalid reshare of unmasked parameters"); + .expect("invalid reshare of unmasked parameters"); - let nodes_involved_in_resharing: Nodes = source_subnet_nodes - .into_receivers(&source_receivers) - .chain(target_subnet_nodes.into_iter()) - .collect(); - let reshared_key_transcript = nodes_involved_in_resharing - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - let target_tecdsa_master_public_key = - get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); + let nodes_involved_in_resharing: Nodes = source_subnet_nodes + .into_receivers(&source_receivers) + .chain(target_subnet_nodes.into_iter()) + .collect(); + let reshared_key_transcript = nodes_involved_in_resharing + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let target_tecdsa_master_public_key = + get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); - assert_eq!( - source_tecdsa_master_public_key, - target_tecdsa_master_public_key - ); + assert_eq!( + source_tecdsa_master_public_key, + target_tecdsa_master_public_key + ); + } } #[test] @@ -4062,34 +4028,31 @@ mod reshare_key_transcript { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let tecdsa_master_public_key = - get_tecdsa_master_public_key(&key_transcript).expect("valid public key"); - let new_dealers = receivers.get().clone(); - let new_receivers = dealers.get().clone(); - let reshare_params = IDkgTranscriptParams::new( - random_transcript_id(rng), - new_dealers, - new_receivers, - key_transcript.registry_version, - key_transcript.algorithm_id, - IDkgTranscriptOperation::ReshareOfUnmasked(key_transcript), - ) - .expect("invalid reshare of unmasked parameters"); - let reshared_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - let reshared_tecdsa_master_public_key = - get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let tecdsa_master_public_key = + get_tecdsa_master_public_key(&key_transcript).expect("valid public key"); + + let new_dealers = receivers.get().clone(); + let new_receivers = dealers.get().clone(); + let reshare_params = IDkgTranscriptParams::new( + random_transcript_id(rng), + new_dealers, + new_receivers, + key_transcript.registry_version, + key_transcript.algorithm_id, + IDkgTranscriptOperation::ReshareOfUnmasked(key_transcript), + ) + .expect("invalid reshare of unmasked parameters"); + let reshared_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let reshared_tecdsa_master_public_key = + get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); - assert_eq!(tecdsa_master_public_key, reshared_tecdsa_master_public_key); + assert_eq!(tecdsa_master_public_key, reshared_tecdsa_master_public_key); + } } #[test] @@ -4099,44 +4062,41 @@ mod reshare_key_transcript { let mut env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let tecdsa_master_public_key = - get_tecdsa_master_public_key(&key_transcript).expect("valid public key"); - - let receivers_with_new_node_ids: BTreeSet<_> = { - let mut new_receivers = receivers.get().clone(); - let num_new_nodes = rng.gen_range(1..10); - let new_random_node_ids = n_random_node_ids(num_new_nodes, rng); - for new_node_id in new_random_node_ids.iter() { - env.add_node(Node::new(*new_node_id, Arc::clone(&env.registry), rng)); - assert!(new_receivers.insert(*new_node_id)); - } - env.registry.reload(); - new_receivers - }; - let reshare_params = IDkgTranscriptParams::new( - random_transcript_id(rng), - receivers.get().clone(), - receivers_with_new_node_ids, - key_transcript.registry_version, - key_transcript.algorithm_id, - IDkgTranscriptOperation::ReshareOfUnmasked(key_transcript), - ) - .expect("invalid reshare of unmasked parameters"); - let reshared_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - let reshared_tecdsa_master_public_key = - get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let tecdsa_master_public_key = + get_tecdsa_master_public_key(&key_transcript).expect("valid public key"); + + let receivers_with_new_node_ids: BTreeSet<_> = { + let mut new_receivers = receivers.get().clone(); + let num_new_nodes = rng.gen_range(1..10); + let new_random_node_ids = n_random_node_ids(num_new_nodes, rng); + for new_node_id in new_random_node_ids.iter() { + env.add_node(Node::new(*new_node_id, Arc::clone(&env.registry), rng)); + assert!(new_receivers.insert(*new_node_id)); + } + env.registry.reload(); + new_receivers + }; + + let reshare_params = IDkgTranscriptParams::new( + random_transcript_id(rng), + receivers.get().clone(), + receivers_with_new_node_ids, + key_transcript.registry_version, + key_transcript.algorithm_id, + IDkgTranscriptOperation::ReshareOfUnmasked(key_transcript), + ) + .expect("invalid reshare of unmasked parameters"); + let reshared_key_transcript = env + .nodes + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let reshared_tecdsa_master_public_key = + get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); - assert_eq!(tecdsa_master_public_key, reshared_tecdsa_master_public_key); + assert_eq!(tecdsa_master_public_key, reshared_tecdsa_master_public_key); + } } #[test] @@ -4146,44 +4106,41 @@ mod reshare_key_transcript { let env = CanisterThresholdSigTestEnvironment::new(subnet_size, rng); let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let tecdsa_master_public_key = - get_tecdsa_master_public_key(&key_transcript).expect("valid public key"); - let receivers_without_removed_receiver = { - let num_receivers_to_remove = rng.gen_range(1..=receivers.get().len() - 1); - let removed_receivers = env + for alg in AlgorithmId::all_threshold_ecdsa_algorithms() { + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let tecdsa_master_public_key = + get_tecdsa_master_public_key(&key_transcript).expect("valid public key"); + + let receivers_without_removed_receiver = { + let num_receivers_to_remove = rng.gen_range(1..=receivers.get().len() - 1); + let removed_receivers = env + .nodes + .receivers(&receivers) + .choose_multiple(rng, num_receivers_to_remove); + let mut new_receivers = receivers.get().clone(); + for removed_receiver in removed_receivers.iter() { + assert!(new_receivers.remove(&removed_receiver.id())); + } + new_receivers + }; + let reshare_params = IDkgTranscriptParams::new( + random_transcript_id(rng), + receivers.get().clone(), + receivers_without_removed_receiver, + key_transcript.registry_version, + key_transcript.algorithm_id, + IDkgTranscriptOperation::ReshareOfUnmasked(key_transcript), + ) + .expect("invalid reshare of unmasked parameters"); + let reshared_key_transcript = env .nodes - .receivers(&receivers) - .choose_multiple(rng, num_receivers_to_remove); - let mut new_receivers = receivers.get().clone(); - for removed_receiver in removed_receivers.iter() { - assert!(new_receivers.remove(&removed_receiver.id())); - } - new_receivers - }; - let reshare_params = IDkgTranscriptParams::new( - random_transcript_id(rng), - receivers.get().clone(), - receivers_without_removed_receiver, - key_transcript.registry_version, - key_transcript.algorithm_id, - IDkgTranscriptOperation::ReshareOfUnmasked(key_transcript), - ) - .expect("invalid reshare of unmasked parameters"); - let reshared_key_transcript = env - .nodes - .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); - let reshared_tecdsa_master_public_key = - get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); + .run_idkg_and_create_and_verify_transcript(&reshare_params, rng); + let reshared_tecdsa_master_public_key = + get_tecdsa_master_public_key(&reshared_key_transcript).expect("valid public key"); - assert_eq!(tecdsa_master_public_key, reshared_tecdsa_master_public_key); + assert_eq!(tecdsa_master_public_key, reshared_tecdsa_master_public_key); + } } } @@ -4239,6 +4196,7 @@ fn corrupt_ecc_scalar(value: &EccScalar) -> EccScalar { } fn environment_and_transcript_for_complaint( + alg: AlgorithmId, rng: &mut R, ) -> ( CanisterThresholdSigTestEnvironment, @@ -4262,12 +4220,7 @@ fn environment_and_transcript_for_complaint( rng, ); - let params = env.params_for_random_sharing( - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); + let params = env.params_for_random_sharing(&dealers, &receivers, alg, rng); let transcript = env .nodes .run_idkg_and_create_and_verify_transcript(¶ms, rng); @@ -4339,6 +4292,7 @@ fn generate_complaints<'a, R: RngCore + CryptoRng>( fn environment_with_sig_inputs( subnet_size_range: S, + alg: AlgorithmId, rng: &mut R, ) -> ( CanisterThresholdSigTestEnvironment, @@ -4355,21 +4309,9 @@ where let (dealers, receivers) = env.choose_dealers_and_receivers(&IDkgParticipants::RandomForThresholdSignature, rng); - let key_transcript = generate_key_transcript( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - rng, - ); - let quadruple = generate_presig_quadruple( - &env, - &dealers, - &receivers, - AlgorithmId::ThresholdEcdsaSecp256k1, - &key_transcript, - rng, - ); + let key_transcript = generate_key_transcript(&env, &dealers, &receivers, alg, rng); + let quadruple = + generate_presig_quadruple(&env, &dealers, &receivers, alg, &key_transcript, rng); let inputs = { let derivation_path = ExtendedDerivationPath { diff --git a/rs/types/types/src/crypto.rs b/rs/types/types/src/crypto.rs index fd55c57120f..214e7081513 100644 --- a/rs/types/types/src/crypto.rs +++ b/rs/types/types/src/crypto.rs @@ -148,6 +148,16 @@ pub enum AlgorithmId { ThresholdEcdsaSecp256r1 = 17, } +impl AlgorithmId { + pub const fn all_threshold_ecdsa_algorithms() -> [AlgorithmId; 2] { + [Self::ThresholdEcdsaSecp256r1, Self::ThresholdEcdsaSecp256k1] + } + + pub fn is_threshold_ecdsa(&self) -> bool { + Self::all_threshold_ecdsa_algorithms().contains(self) + } +} + impl From for u8 { fn from(value: AlgorithmId) -> Self { u8::try_from(value as isize).expect("could not convert AlgorithmId to u8") diff --git a/rs/types/types/src/crypto/canister_threshold_sig.rs b/rs/types/types/src/crypto/canister_threshold_sig.rs index 0cc55c78643..57d471760e0 100644 --- a/rs/types/types/src/crypto/canister_threshold_sig.rs +++ b/rs/types/types/src/crypto/canister_threshold_sig.rs @@ -324,6 +324,9 @@ pub struct ThresholdEcdsaSigInputs { // The byte length of an hashed message for ECDSA signatures over the curve secp256k1. pub const ECDSA_SECP256K1_HASH_BYTE_LENGTH: usize = 32; +// The byte length of an hashed message for ECDSA signatures over the curve secp256r1. +pub const ECDSA_SECP256R1_HASH_BYTE_LENGTH: usize = 32; + impl Display for ThresholdEcdsaSigInputs { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) @@ -446,6 +449,12 @@ impl ThresholdEcdsaSigInputs { } Ok(()) } + AlgorithmId::ThresholdEcdsaSecp256r1 => { + if hashed_message.len() != ECDSA_SECP256R1_HASH_BYTE_LENGTH { + return Err(error::ThresholdEcdsaSigInputsCreationError::InvalidHashLength); + } + Ok(()) + } _ => Err(error::ThresholdEcdsaSigInputsCreationError::UnsupportedAlgorithm), } } diff --git a/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs b/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs index 2dd1886e4b7..f9a2228ea98 100644 --- a/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs +++ b/rs/types/types/src/crypto/canister_threshold_sig/idkg.rs @@ -302,8 +302,10 @@ pub struct IDkgTranscriptParams { dealers: IDkgDealers, receivers: IDkgReceivers, registry_version: RegistryVersion, - /// Identifies the cryptographic signature scheme used in the protocol. - /// Currently only [`AlgorithmId::ThresholdEcdsaSecp256k1`] is supported. + /// Identifies the cryptographic signature scheme used in the + /// protocol. Currently only + /// [`AlgorithmId::ThresholdEcdsaSecp256k1`] and + /// [`AlgorithmId::ThresholdEcdsaSecp256r1`] are supported. algorithm_id: AlgorithmId, /// Mode of operation for this current execution of the protocol. operation_type: IDkgTranscriptOperation, @@ -498,6 +500,7 @@ impl IDkgTranscriptParams { fn ensure_algorithm_id_supported(&self) -> Result<(), IDkgParamsValidationError> { match self.algorithm_id { AlgorithmId::ThresholdEcdsaSecp256k1 => Ok(()), + AlgorithmId::ThresholdEcdsaSecp256r1 => Ok(()), _ => Err(IDkgParamsValidationError::UnsupportedAlgorithmId { algorithm_id: self.algorithm_id, }), diff --git a/rs/types/types/src/crypto/tests.rs b/rs/types/types/src/crypto/tests.rs index 245ca76fa57..fca53718ce4 100644 --- a/rs/types/types/src/crypto/tests.rs +++ b/rs/types/types/src/crypto/tests.rs @@ -113,6 +113,17 @@ fn should_not_have_any_algorithm_id_that_does_not_fit_into_u8() { } } +#[test] +fn should_have_consistent_logic_for_tecdsa_algorithm_identification() { + let tecdsa_algos = AlgorithmId::all_threshold_ecdsa_algorithms(); + + for algorithm_id in AlgorithmId::iter() { + let is_tecdsa = algorithm_id.is_threshold_ecdsa(); + + assert_eq!(is_tecdsa, tecdsa_algos.contains(&algorithm_id)); + } +} + #[cfg(test)] impl KeyPurpose { fn as_str(&self) -> &'static str {