Skip to content

Commit

Permalink
Merge branch 'jack/crp-2228' into 'master'
Browse files Browse the repository at this point in the history
feat(crypto): CRP-2228 Add support at the crypto component layer for P-256 threshold ECDSA

 

See merge request dfinity-lab/public/ic!15281
  • Loading branch information
randombit committed Oct 27, 2023
2 parents bac9eb1 + 15d6a68 commit 843f26d
Show file tree
Hide file tree
Showing 15 changed files with 3,096 additions and 3,007 deletions.
10 changes: 8 additions & 2 deletions rs/consensus/src/ecdsa/payload_builder.rs
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions rs/consensus/src/ecdsa/utils.rs
Expand Up @@ -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()),
Expand All @@ -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 {
Expand Down
Expand Up @@ -148,6 +148,10 @@ mod stability_tests {
input: (AlgorithmId::MegaSecp256k1, bytes),
expected: "93549663cba48293c1d9a92de585a49581e05af84563aecd47fb7ab5fe9745c3",
},
ParameterizedTest {
input: (AlgorithmId::ThresholdEcdsaSecp256r1, bytes),
expected: "001b0b2aa06c51280e5267b7c9a5c5aa1691dcec75622eaa5c30d2ed08c5f25a",
},
];

for test in &tests {
Expand Down
Expand Up @@ -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();
Expand Down
21 changes: 13 additions & 8 deletions 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::{
Expand All @@ -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(),
}
}
Expand Down Expand Up @@ -230,8 +234,8 @@ pub enum MasterPublicKeyExtractionError {
pub fn get_tecdsa_master_public_key(
idkg_transcript: &IDkgTranscript,
) -> Result<MasterEcdsaPublicKey, MasterPublicKeyExtractionError> {
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| {
Expand All @@ -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),
)),
))
}
}

Expand Down
138 changes: 86 additions & 52 deletions rs/crypto/src/sign/canister_threshold_sig/ecdsa/tests.rs
Expand Up @@ -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"),
);
Expand All @@ -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);
}
}
}

Expand Down

0 comments on commit 843f26d

Please sign in to comment.