diff --git a/pallets/acurast/Cargo.toml b/pallets/acurast/Cargo.toml index 04978fce..6e30c2f4 100644 --- a/pallets/acurast/Cargo.toml +++ b/pallets/acurast/Cargo.toml @@ -20,6 +20,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/substrate", default- frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.26" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.26" } sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.26" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.26" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.26" } # Attestation @@ -36,7 +37,8 @@ hex-literal = "0.3" sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.26" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.26" } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.26" } + +acurast-p256-crypto = { path = "../../p256-crypto" } [features] default = ["std"] @@ -54,5 +56,6 @@ std = [ "sha2/std", "num-bigint/std", "ecdsa-vendored/std", + "sp-io/std", ] try-runtime = [ "frame-support/try-runtime" ] diff --git a/pallets/acurast/src/attestation.rs b/pallets/acurast/src/attestation.rs index 5ce7a538..915aeabf 100644 --- a/pallets/acurast/src/attestation.rs +++ b/pallets/acurast/src/attestation.rs @@ -98,19 +98,19 @@ const RSA_PBK: ObjectIdentifier = oid!(1, 2, 840, 113549, 1, 1, 1); const ECDSA_PBK: ObjectIdentifier = oid!(1, 2, 840, 10045, 2, 1); #[derive(Clone)] -enum PublicKey { +pub enum PublicKey { RSA(RSAPbk), ECDSA(ECDSACurve), } #[derive(Clone)] -struct RSAPbk { +pub struct RSAPbk { exponent: BigUint, modulus: BigUint, } #[derive(Clone)] -enum ECDSACurve { +pub enum ECDSACurve { CurveP256(VerifyingKey), CurveP384(p384::AffinePoint), } @@ -273,7 +273,7 @@ pub fn validate_certificate_chain_root( /// - the next certificate's public key signs the next one and so on... pub fn validate_certificate_chain<'a>( chain: &'a CertificateChainInput, -) -> Result<(Vec, TBSCertificate<'a>), ValidationError> { +) -> Result<(Vec, TBSCertificate<'a>, PublicKey), ValidationError> { let mut cert_ids = Vec::::new(); let fold_result = chain.iter().try_fold::<_, _, Result<_, ValidationError>>( (Option::::None, Option::::None), @@ -297,9 +297,10 @@ pub fn validate_certificate_chain<'a>( )?; let last_cert = fold_result.1.ok_or(ValidationError::ChainTooShort)?; + let last_cert_pbk = fold_result.0.ok_or(ValidationError::MissingPublicKey)?; // if the chain is non-empty as ensured above, we know that we always have Some certificate in option - Ok((cert_ids, last_cert.tbs_certificate)) + Ok((cert_ids, last_cert.tbs_certificate, last_cert_pbk)) } /// The list of trusted root certificates, as decoded bytes arrays. [Source](https://developer.android.com/training/articles/security-key-attestation#root_certificate) @@ -369,7 +370,7 @@ mod tests { ]; let decoded_chain = decode_certificate_chain(&chain); validate_certificate_chain_root(&decoded_chain)?; - let (_, cert) = validate_certificate_chain(&decoded_chain)?; + let (_, cert, _) = validate_certificate_chain(&decoded_chain)?; let key_description = extract_attestation(cert.extensions)?; match &key_description { KeyDescription::V100(key_description) => { @@ -391,7 +392,7 @@ mod tests { ]; let decoded_chain = decode_certificate_chain(&chain); validate_certificate_chain_root(&decoded_chain).expect("validating root failed"); - let (_, cert) = + let (_, cert, _) = validate_certificate_chain(&decoded_chain).expect("validating chain failed"); let key_description = extract_attestation(cert.extensions)?; match &key_description { diff --git a/pallets/acurast/src/attestation/error.rs b/pallets/acurast/src/attestation/error.rs index 7d4028e0..11d4b850 100644 --- a/pallets/acurast/src/attestation/error.rs +++ b/pallets/acurast/src/attestation/error.rs @@ -20,6 +20,7 @@ pub enum ValidationError { ParseP256PublicKey, ParseP384PublicKey, MissingECDSAAlgorithmTyp, + MissingPublicKey, InvalidSignatureEncoding, InvalidSignature, UnsupportedSignatureAlgorithm, diff --git a/pallets/acurast/src/lib.rs b/pallets/acurast/src/lib.rs index 678d54a3..b34ca523 100644 --- a/pallets/acurast/src/lib.rs +++ b/pallets/acurast/src/lib.rs @@ -143,12 +143,16 @@ pub mod pallet { CannotGetCertificateId, /// Failed to convert the attestation to its bounded type. AttestationToBoundedTypeConversionFailed, - /// Timestamp error + /// Timestamp error. FailedTimestampConversion, - /// Certificate was revoked + /// Certificate was revoked. RevokedCertificate, - /// Origin is not allowed to update the certificate revocation list + /// Origin is not allowed to update the certificate revocation list. CertificateRevocationListUpdateNotAllowed, + /// The attestation was issued for an unsupported public key type. + UnsupportedAttestationPublicKeyType, + /// The submitted attestation public key does not match the source. + AttestationPublicKeyDoesNotMatchSource, } #[pallet::hooks] @@ -302,7 +306,7 @@ pub mod pallet { Error::::CertificateChainTooShort, ); - let attestation = validate_and_extract_attestation::(&attestation_chain)?; + let attestation = validate_and_extract_attestation::(&who, &attestation_chain)?; ensure_not_expired::(&attestation)?; ensure_not_revoked::(&attestation)?; diff --git a/pallets/acurast/src/mock.rs b/pallets/acurast/src/mock.rs index 2909a7af..766cf8fd 100644 --- a/pallets/acurast/src/mock.rs +++ b/pallets/acurast/src/mock.rs @@ -1,10 +1,10 @@ use hex_literal::hex; use sp_io; -use sp_runtime::{testing::Header, traits::IdentityLookup}; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use crate::{AttestationChain, Fulfillment, JobRegistration, Script, SerialNumber}; -type AccountId = u64; +type AccountId = AccountId32; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -31,7 +31,7 @@ impl frame_system::Config for Test { type Call = Call; type Hash = sp_core::H256; type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type Event = Event; @@ -50,7 +50,7 @@ impl frame_system::Config for Test { frame_support::parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max(1024); pub const MinimumPeriod: u64 = 6000; - pub AllowedRevocationListUpdate: Vec = vec![1]; + pub AllowedRevocationListUpdate: Vec = vec![alice_account_id()]; pub static ExistentialDeposit: u64 = 0; } @@ -223,3 +223,27 @@ pub fn invalid_attestation_chain_3() -> AttestationChain { pub fn cert_serial_number() -> SerialNumber { hex!("15905857467176635834").to_vec().try_into().unwrap() } + +pub fn processor_account_id() -> AccountId { + hex!("b8bc25a2b4c0386b8892b43e435b71fe11fa50533935f027949caf04bcce4694").into() +} + +pub fn alice_account_id() -> AccountId { + [0; 32].into() +} + +pub fn bob_account_id() -> AccountId { + [1; 32].into() +} + +pub fn charlie_account_id() -> AccountId { + [2; 32].into() +} + +pub fn dave_account_id() -> AccountId { + [3; 32].into() +} + +pub fn eve_account_id() -> AccountId { + [4; 32].into() +} diff --git a/pallets/acurast/src/tests.rs b/pallets/acurast/src/tests.rs index e84ed3a8..e15d7de3 100644 --- a/pallets/acurast/src/tests.rs +++ b/pallets/acurast/src/tests.rs @@ -12,30 +12,36 @@ fn test_job_registration() { ExtBuilder::default().build().execute_with(|| { let registration = job_registration(None, false); assert_ok!(Acurast::register( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.clone(), )); assert_eq!( Some(registration.clone()), - Acurast::stored_job_registration(1, registration.script.clone()) + Acurast::stored_job_registration(alice_account_id(), registration.script.clone()) ); assert_ok!(Acurast::deregister( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.script.clone() )); assert_eq!( None, - Acurast::stored_job_registration(1, registration.script.clone()) + Acurast::stored_job_registration(alice_account_id(), registration.script.clone()) ); assert_eq!( events(), [ - Event::Acurast(crate::Event::JobRegistrationStored(registration.clone(), 1)), - Event::Acurast(crate::Event::JobRegistrationRemoved(registration.script, 1)) + Event::Acurast(crate::Event::JobRegistrationStored( + registration.clone(), + alice_account_id() + )), + Event::Acurast(crate::Event::JobRegistrationRemoved( + registration.script, + alice_account_id() + )) ] ); }); @@ -46,13 +52,16 @@ fn test_job_registration_failure_1() { ExtBuilder::default().build().execute_with(|| { let registration = invalid_job_registration_1(); assert_err!( - Acurast::register(Origin::signed(1).into(), registration.clone()), + Acurast::register( + Origin::signed(alice_account_id()).into(), + registration.clone() + ), Error::::InvalidScriptValue ); assert_eq!( None, - Acurast::stored_job_registration(1, registration.script) + Acurast::stored_job_registration(alice_account_id(), registration.script) ); assert_eq!(events(), []); @@ -64,13 +73,16 @@ fn test_job_registration_failure_2() { ExtBuilder::default().build().execute_with(|| { let registration = invalid_job_registration_2(); assert_err!( - Acurast::register(Origin::signed(1).into(), registration.clone()), + Acurast::register( + Origin::signed(alice_account_id()).into(), + registration.clone() + ), Error::::InvalidScriptValue ); assert_eq!( None, - Acurast::stored_job_registration(1, registration.script) + Acurast::stored_job_registration(alice_account_id(), registration.script) ); assert_eq!(events(), []); @@ -80,26 +92,41 @@ fn test_job_registration_failure_2() { #[test] fn test_job_registration_failure_3() { ExtBuilder::default().build().execute_with(|| { - let registration_1 = job_registration(Some(vec![1, 2, 3, 4, 12]), false); + let registration_1 = job_registration( + Some(vec![ + alice_account_id(), + bob_account_id(), + charlie_account_id(), + dave_account_id(), + eve_account_id(), + ]), + false, + ); let registration_2 = job_registration(Some(vec![]), false); assert_err!( - Acurast::register(Origin::signed(1).into(), registration_1.clone()), + Acurast::register( + Origin::signed(alice_account_id()).into(), + registration_1.clone() + ), Error::::TooManyAllowedSources ); assert_eq!( None, - Acurast::stored_job_registration(1, registration_1.script) + Acurast::stored_job_registration(alice_account_id(), registration_1.script) ); assert_err!( - Acurast::register(Origin::signed(1).into(), registration_2.clone()), + Acurast::register( + Origin::signed(alice_account_id()).into(), + registration_2.clone() + ), Error::::TooFewAllowedSources ); assert_eq!( None, - Acurast::stored_job_registration(1, registration_2.script) + Acurast::stored_job_registration(alice_account_id(), registration_2.script) ); assert_eq!(events(), []); @@ -110,52 +137,53 @@ fn test_job_registration_failure_3() { fn test_update_allowed_sources() { ExtBuilder::default().build().execute_with(|| { let registration_1 = job_registration(None, false); - let registration_2 = job_registration(Some(vec![1, 2]), false); + let registration_2 = + job_registration(Some(vec![alice_account_id(), bob_account_id()]), false); let updates_1 = vec![ AllowedSourcesUpdate { operation: ListUpdateOperation::Add, - account_id: 1, + account_id: alice_account_id(), }, AllowedSourcesUpdate { operation: ListUpdateOperation::Add, - account_id: 2, + account_id: bob_account_id(), }, ]; let updates_2 = vec![ AllowedSourcesUpdate { operation: ListUpdateOperation::Remove, - account_id: 1, + account_id: alice_account_id(), }, AllowedSourcesUpdate { operation: ListUpdateOperation::Remove, - account_id: 2, + account_id: bob_account_id(), }, ]; assert_ok!(Acurast::register( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration_1.clone(), )); assert_ok!(Acurast::update_allowed_sources( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration_1.script.clone(), updates_1.clone() )); assert_eq!( Some(registration_2.clone()), - Acurast::stored_job_registration(1, ®istration_1.script) + Acurast::stored_job_registration(alice_account_id(), ®istration_1.script) ); assert_ok!(Acurast::update_allowed_sources( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration_1.script.clone(), updates_2.clone() )); assert_eq!( Some(registration_1.clone()), - Acurast::stored_job_registration(1, ®istration_1.script) + Acurast::stored_job_registration(alice_account_id(), ®istration_1.script) ); assert_eq!( @@ -163,15 +191,15 @@ fn test_update_allowed_sources() { [ Event::Acurast(crate::Event::JobRegistrationStored( registration_1.clone(), - 1 + alice_account_id() )), Event::Acurast(crate::Event::AllowedSourcesUpdated( - 1, + alice_account_id(), registration_1, updates_1 )), Event::Acurast(crate::Event::AllowedSourcesUpdated( - 1, + alice_account_id(), registration_2, updates_2 )) @@ -182,20 +210,28 @@ fn test_update_allowed_sources() { #[test] fn test_update_allowed_sources_failure() { - let registration = job_registration(Some(vec![1, 2, 3, 4]), false); + let registration = job_registration( + Some(vec![ + alice_account_id(), + bob_account_id(), + charlie_account_id(), + dave_account_id(), + ]), + false, + ); let updates = vec![AllowedSourcesUpdate { operation: ListUpdateOperation::Add, - account_id: 12, + account_id: eve_account_id(), }]; ExtBuilder::default().build().execute_with(|| { assert_ok!(Acurast::register( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.clone(), )); assert_err!( Acurast::update_allowed_sources( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.script.clone(), updates.clone() ), @@ -204,14 +240,14 @@ fn test_update_allowed_sources_failure() { assert_eq!( Some(registration.clone()), - Acurast::stored_job_registration(1, ®istration.script) + Acurast::stored_job_registration(alice_account_id(), ®istration.script) ); assert_eq!( events(), [Event::Acurast(crate::Event::JobRegistrationStored( registration.clone(), - 1 + alice_account_id() )),] ); }); @@ -226,24 +262,27 @@ fn test_fulfill() { }; ExtBuilder::default().build().execute_with(|| { assert_ok!(Acurast::register( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.clone(), )); assert_ok!(Acurast::fulfill( - Origin::signed(2).into(), + Origin::signed(bob_account_id()).into(), fulfillment.clone(), - 1 + alice_account_id() )); assert_eq!( events(), [ - Event::Acurast(crate::Event::JobRegistrationStored(registration.clone(), 1)), + Event::Acurast(crate::Event::JobRegistrationStored( + registration.clone(), + alice_account_id() + )), Event::Acurast(crate::Event::ReceivedFulfillment( - 2, + bob_account_id(), fulfillment, registration, - 1 + alice_account_id() )), ] ); @@ -258,7 +297,11 @@ fn test_fulfill_failure_1() { }; ExtBuilder::default().build().execute_with(|| { assert_err!( - Acurast::fulfill(Origin::signed(2).into(), fulfillment.clone(), 1), + Acurast::fulfill( + Origin::signed(alice_account_id()).into(), + fulfillment.clone(), + bob_account_id() + ), Error::::JobRegistrationNotFound ); @@ -272,11 +315,15 @@ fn test_fulfill_failure_2() { let fulfillment = fulfillment_for(®istration); ExtBuilder::default().build().execute_with(|| { assert_ok!(Acurast::register( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.clone(), )); assert_err!( - Acurast::fulfill(Origin::signed(2).into(), fulfillment.clone(), 1), + Acurast::fulfill( + Origin::signed(bob_account_id()).into(), + fulfillment.clone(), + alice_account_id() + ), Error::::FulfillSourceNotVerified ); @@ -284,7 +331,7 @@ fn test_fulfill_failure_2() { events(), [Event::Acurast(crate::Event::JobRegistrationStored( registration.clone(), - 1 + alice_account_id() ))] ); }); @@ -292,15 +339,19 @@ fn test_fulfill_failure_2() { #[test] fn test_fulfill_failure_3() { - let registration = job_registration(Some(vec![3]), false); + let registration = job_registration(Some(vec![charlie_account_id()]), false); let fulfillment = fulfillment_for(®istration); ExtBuilder::default().build().execute_with(|| { assert_ok!(Acurast::register( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), registration.clone(), )); assert_err!( - Acurast::fulfill(Origin::signed(2).into(), fulfillment.clone(), 1), + Acurast::fulfill( + Origin::signed(bob_account_id()).into(), + fulfillment.clone(), + alice_account_id() + ), Error::::FulfillSourceNotAllowed ); @@ -308,7 +359,7 @@ fn test_fulfill_failure_3() { events(), [Event::Acurast(crate::Event::JobRegistrationStored( registration.clone(), - 1 + alice_account_id() ))] ); }); @@ -320,19 +371,23 @@ fn test_submit_attestation() { let chain = attestation_chain(); _ = Timestamp::set(Origin::none(), 1657363915001); assert_ok!(Acurast::submit_attestation( - Origin::signed(1).into(), + Origin::signed(processor_account_id()).into(), chain.clone() )); - let attestation = validate_and_extract_attestation::(&chain).unwrap(); + let attestation = + validate_and_extract_attestation::(&processor_account_id(), &chain).unwrap(); - assert_eq!(Some(attestation.clone()), Acurast::stored_attestation(1)); + assert_eq!( + Some(attestation.clone()), + Acurast::stored_attestation(processor_account_id()) + ); assert_eq!( events(), [Event::Acurast(crate::Event::AttestationStored( attestation, - 1 + processor_account_id() ))] ); }); @@ -347,27 +402,38 @@ fn test_submit_attestation_register_fulfill() { _ = Timestamp::set(Origin::none(), 1657363915001); assert_ok!(Acurast::submit_attestation( - Origin::signed(1).into(), + Origin::signed(processor_account_id()).into(), chain.clone() )); assert_ok!(Acurast::register( - Origin::signed(2).into(), + Origin::signed(bob_account_id()).into(), registration.clone() )); - assert_ok!(Acurast::fulfill(Origin::signed(1), fulfillment.clone(), 2)); + assert_ok!(Acurast::fulfill( + Origin::signed(processor_account_id()), + fulfillment.clone(), + bob_account_id() + )); - let attestation = validate_and_extract_attestation::(&chain).unwrap(); + let attestation = + validate_and_extract_attestation::(&processor_account_id(), &chain).unwrap(); assert_eq!( events(), [ - Event::Acurast(crate::Event::AttestationStored(attestation, 1)), - Event::Acurast(crate::Event::JobRegistrationStored(registration.clone(), 2)), + Event::Acurast(crate::Event::AttestationStored( + attestation, + processor_account_id() + )), + Event::Acurast(crate::Event::JobRegistrationStored( + registration.clone(), + bob_account_id() + )), Event::Acurast(crate::Event::ReceivedFulfillment( - 1, + processor_account_id(), fulfillment, registration, - 2 + bob_account_id() )), ] ); @@ -380,29 +446,38 @@ fn test_submit_attestation_failure_1() { let chain = invalid_attestation_chain_1(); assert_err!( - Acurast::submit_attestation(Origin::signed(1).into(), chain.clone()), + Acurast::submit_attestation( + Origin::signed(processor_account_id()).into(), + chain.clone() + ), Error::::CertificateChainTooShort ); - assert_eq!(None, Acurast::stored_attestation(1)); + assert_eq!(None, Acurast::stored_attestation(processor_account_id())); let chain = invalid_attestation_chain_2(); assert_err!( - Acurast::submit_attestation(Origin::signed(1).into(), chain.clone()), + Acurast::submit_attestation( + Origin::signed(processor_account_id()).into(), + chain.clone() + ), Error::::RootCertificateValidationFailed ); - assert_eq!(None, Acurast::stored_attestation(1)); + assert_eq!(None, Acurast::stored_attestation(processor_account_id())); let chain = invalid_attestation_chain_3(); assert_err!( - Acurast::submit_attestation(Origin::signed(1).into(), chain.clone()), + Acurast::submit_attestation( + Origin::signed(processor_account_id()).into(), + chain.clone() + ), Error::::CertificateChainValidationFailed ); - assert_eq!(None, Acurast::stored_attestation(1)); + assert_eq!(None, Acurast::stored_attestation(processor_account_id())); assert_eq!(events(), []); }); @@ -415,11 +490,14 @@ fn test_submit_attestation_failure_2() { _ = Timestamp::set(Origin::none(), 1657363914000); assert_err!( - Acurast::submit_attestation(Origin::signed(1).into(), chain.clone()), + Acurast::submit_attestation( + Origin::signed(processor_account_id()).into(), + chain.clone() + ), Error::::AttestationCertificateNotValid ); - assert_eq!(None, Acurast::stored_attestation(1)); + assert_eq!(None, Acurast::stored_attestation(processor_account_id())); assert_eq!(events(), []); }); @@ -432,11 +510,14 @@ fn test_submit_attestation_failure_3() { _ = Timestamp::set(Origin::none(), 1842739199001); assert_err!( - Acurast::submit_attestation(Origin::signed(1).into(), chain.clone()), + Acurast::submit_attestation( + Origin::signed(processor_account_id()).into(), + chain.clone() + ), Error::::AttestationCertificateNotValid ); - assert_eq!(None, Acurast::stored_attestation(1)); + assert_eq!(None, Acurast::stored_attestation(processor_account_id())); assert_eq!(events(), []); }); @@ -450,7 +531,7 @@ fn test_update_revocation_list() { cert_serial_number: cert_serial_number(), }]; assert_ok!(Acurast::update_certificate_revocation_list( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), updates_1.clone(), )); assert_eq!( @@ -463,7 +544,7 @@ fn test_update_revocation_list() { cert_serial_number: cert_serial_number(), }]; assert_ok!(Acurast::update_certificate_revocation_list( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), updates_2.clone(), )); assert_eq!( @@ -473,7 +554,7 @@ fn test_update_revocation_list() { assert_err!( Acurast::update_certificate_revocation_list( - Origin::signed(2).into(), + Origin::signed(bob_account_id()).into(), updates_1.clone(), ), Error::::CertificateRevocationListUpdateNotAllowed @@ -486,8 +567,14 @@ fn test_update_revocation_list() { assert_eq!( events(), [ - Event::Acurast(crate::Event::CertificateRecovationListUpdated(1, updates_1)), - Event::Acurast(crate::Event::CertificateRecovationListUpdated(1, updates_2)) + Event::Acurast(crate::Event::CertificateRecovationListUpdated( + alice_account_id(), + updates_1 + )), + Event::Acurast(crate::Event::CertificateRecovationListUpdated( + alice_account_id(), + updates_2 + )) ] ); }); @@ -501,21 +588,24 @@ fn test_update_revocation_list_submit_attestation() { cert_serial_number: cert_serial_number(), }]; assert_ok!(Acurast::update_certificate_revocation_list( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), updates.clone(), )); let chain = attestation_chain(); _ = Timestamp::set(Origin::none(), 1657363915001); assert_err!( - Acurast::submit_attestation(Origin::signed(1).into(), chain.clone()), + Acurast::submit_attestation( + Origin::signed(processor_account_id()).into(), + chain.clone() + ), Error::::RevokedCertificate ); assert_eq!( events(), [Event::Acurast( - crate::Event::CertificateRecovationListUpdated(1, updates) + crate::Event::CertificateRecovationListUpdated(alice_account_id(), updates) ),] ); }); @@ -533,30 +623,44 @@ fn test_update_revocation_list_fulfill() { let fulfillment = fulfillment_for(®istration); _ = Timestamp::set(Origin::none(), 1657363915001); assert_ok!(Acurast::submit_attestation( - Origin::signed(1).into(), + Origin::signed(processor_account_id()).into(), chain.clone() )); assert_ok!(Acurast::update_certificate_revocation_list( - Origin::signed(1).into(), + Origin::signed(alice_account_id()).into(), updates.clone(), )); assert_ok!(Acurast::register( - Origin::signed(2).into(), + Origin::signed(bob_account_id()).into(), registration.clone() )); assert_err!( - Acurast::fulfill(Origin::signed(1), fulfillment.clone(), 2), + Acurast::fulfill( + Origin::signed(processor_account_id()), + fulfillment.clone(), + bob_account_id() + ), Error::::RevokedCertificate ); - let attestation = validate_and_extract_attestation::(&chain).unwrap(); + let attestation = + validate_and_extract_attestation::(&processor_account_id(), &chain).unwrap(); assert_eq!( events(), [ - Event::Acurast(crate::Event::AttestationStored(attestation, 1)), - Event::Acurast(crate::Event::CertificateRecovationListUpdated(1, updates)), - Event::Acurast(crate::Event::JobRegistrationStored(registration.clone(), 2)), + Event::Acurast(crate::Event::AttestationStored( + attestation, + processor_account_id() + )), + Event::Acurast(crate::Event::CertificateRecovationListUpdated( + alice_account_id(), + updates + )), + Event::Acurast(crate::Event::JobRegistrationStored( + registration.clone(), + bob_account_id() + )), ] ); }); diff --git a/pallets/acurast/src/utils.rs b/pallets/acurast/src/utils.rs index 01d4b1d4..e06fb505 100644 --- a/pallets/acurast/src/utils.rs +++ b/pallets/acurast/src/utils.rs @@ -1,7 +1,10 @@ +use codec::Encode; +use frame_support::ensure; use sp_std::prelude::*; use crate::attestation::{ - extract_attestation, validate_certificate_chain, validate_certificate_chain_root, + extract_attestation, validate_certificate_chain, validate_certificate_chain_root, ECDSACurve, + PublicKey, }; use crate::{ Attestation, AttestationChain, AttestationValidity, CertId, Config, Error, IssuerName, @@ -9,13 +12,17 @@ use crate::{ }; pub(crate) fn validate_and_extract_attestation( + source: &T::AccountId, attestation_chain: &AttestationChain, ) -> Result> { validate_certificate_chain_root(&attestation_chain.certificate_chain) .map_err(|_| Error::::RootCertificateValidationFailed)?; - let (cert_ids, cert) = validate_certificate_chain(&attestation_chain.certificate_chain) - .map_err(|_| Error::::CertificateChainValidationFailed)?; + let (cert_ids, cert, public_key) = + validate_certificate_chain(&attestation_chain.certificate_chain) + .map_err(|_| Error::::CertificateChainValidationFailed)?; + + ensure_valid_public_key_for_source(source, &public_key)?; let attestation_validity = AttestationValidity { not_before: cert.validity.not_before.timestamp_millis(), @@ -109,3 +116,26 @@ pub(crate) fn ensure_not_revoked(attestation: &Attestation) -> Result } Ok(()) } + +fn ensure_valid_public_key_for_source( + source: &T::AccountId, + public_key: &PublicKey, +) -> Result<(), Error> { + match public_key { + PublicKey::RSA(_) => Err(Error::::UnsupportedAttestationPublicKeyType), + PublicKey::ECDSA(public_key) => match public_key { + ECDSACurve::CurveP256(public_key) => { + let encoded_source = source.encode(); + let encoded_public_key = + sp_io::hashing::blake2_256(&public_key.to_bytes().to_vec()).to_vec(); + + ensure!( + encoded_source == encoded_public_key, + Error::::AttestationPublicKeyDoesNotMatchSource + ); + Ok(()) + } + ECDSACurve::CurveP384(_) => Err(Error::::UnsupportedAttestationPublicKeyType), + }, + } +}