Skip to content

Commit c6d3de5

Browse files
committed
feat: add adversary scenario test in certificate verifier
- Related to security advisory GHSA-724h-fpm5-4qvr - Details available at GHSA-724h-fpm5-4qvr
1 parent 5369c61 commit c6d3de5

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

mithril-common/src/certificate_chain/certificate_verifier.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,4 +1073,88 @@ mod tests {
10731073

10741074
assert_error_matches!(CertificateVerifierError::CertificateHashUnmatch, error)
10751075
}
1076+
1077+
#[tokio::test]
1078+
async fn verify_certificate_chain_fails_when_adversarial_with_registered_signer_forgery_through_protocol_parameters(
1079+
) {
1080+
// Create an adversarial certificate with a forged multi signature:
1081+
// - with the valid signed message
1082+
// - with the valid aggregate verification key (valid stake distribution)
1083+
// - by a legit adversarial registered signer in the signing stake distribution
1084+
// - with adversarial protocol parameters (phi_f = 1.0, i.e. all lotteries are won by the adversarial signer and quorum is reached)
1085+
fn forge_certificate(
1086+
certificate: Certificate,
1087+
context: &CertificateChainBuilderContext,
1088+
) -> Certificate {
1089+
assert_ne!(
1090+
1.0, certificate.metadata.protocol_parameters.phi_f,
1091+
"Adversarial protocol parameters phi_f should be different from 1.0"
1092+
);
1093+
let fixture = context.fixture;
1094+
let signed_message = certificate.signed_message.to_owned();
1095+
let mut forged_certificate = certificate;
1096+
let mut forged_protocol_parameters = fixture.protocol_parameters();
1097+
forged_protocol_parameters.phi_f = 1.0;
1098+
let forged_single_signatures = fixture
1099+
.signers_fixture()
1100+
.iter()
1101+
.take(1)
1102+
.filter_map(|s| {
1103+
let s_adversary = s
1104+
.to_owned()
1105+
.try_new_with_protocol_parameters(forged_protocol_parameters.clone())
1106+
.unwrap();
1107+
let signature = s_adversary.protocol_signer.sign(signed_message.as_bytes());
1108+
1109+
signature
1110+
})
1111+
.collect::<Vec<_>>();
1112+
let forged_clerk = ProtocolClerk::from_registration(
1113+
&forged_protocol_parameters.clone().into(),
1114+
&fixture.signers_fixture()[0].protocol_closed_key_registration,
1115+
);
1116+
let forged_multi_signature = forged_clerk
1117+
.aggregate(&forged_single_signatures, signed_message.as_bytes())
1118+
.unwrap();
1119+
forged_certificate.signature = CertificateSignature::MultiSignature(
1120+
forged_certificate.signed_entity_type(),
1121+
forged_multi_signature.into(),
1122+
);
1123+
forged_certificate.metadata.protocol_parameters = forged_protocol_parameters;
1124+
1125+
forged_certificate
1126+
}
1127+
1128+
let (total_certificates, certificates_per_epoch) = (7, 2);
1129+
let (fake_certificates, genesis_verifier) = CertificateChainBuilder::new()
1130+
.with_total_certificates(total_certificates)
1131+
.with_certificates_per_epoch(certificates_per_epoch)
1132+
.with_standard_certificate_processor(&|certificate, context| {
1133+
if context.is_last_certificate() {
1134+
forge_certificate(certificate, context)
1135+
} else {
1136+
certificate
1137+
}
1138+
})
1139+
.build();
1140+
let certificate_to_verify = fake_certificates[0].clone();
1141+
let mock_container = MockDependencyInjector::new();
1142+
let mut verifier = mock_container.build_certificate_verifier();
1143+
verifier.certificate_retriever = Arc::new(FakeCertificaterRetriever::from_certificates(
1144+
&fake_certificates,
1145+
));
1146+
1147+
let error = verifier
1148+
.verify_certificate(
1149+
&certificate_to_verify,
1150+
&genesis_verifier.to_verification_key(),
1151+
)
1152+
.await
1153+
.expect_err("verify_certificate_chain should fail");
1154+
1155+
assert_error_matches!(
1156+
CertificateVerifierError::CertificateChainProtocolParametersUnmatch,
1157+
error
1158+
)
1159+
}
10761160
}

0 commit comments

Comments
 (0)