From 65ba534d37c6a0d6a9b84bc8d1a58ba00d83e376 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 21 Mar 2024 17:10:48 +0530 Subject: [PATCH 1/9] add primitive crate for signature verification --- Cargo.lock | 142 ++++++++++++++++-- .../certificate-registry/Cargo.toml | 33 ++++ .../src/host_functions.rs | 43 ++++++ .../certificate-registry/src/lib.rs | 66 ++++++++ .../src/runtime_interface.rs | 19 +++ 5 files changed, 287 insertions(+), 16 deletions(-) create mode 100644 domains/primitives/certificate-registry/Cargo.toml create mode 100644 domains/primitives/certificate-registry/src/host_functions.rs create mode 100644 domains/primitives/certificate-registry/src/lib.rs create mode 100644 domains/primitives/certificate-registry/src/runtime_interface.rs diff --git a/Cargo.lock b/Cargo.lock index c8e8c24b74..e63637bfa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -728,8 +728,24 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", + "asn1-rs-derive 0.4.0", + "asn1-rs-impl 0.1.0", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" +dependencies = [ + "asn1-rs-derive 0.5.0", + "asn1-rs-impl 0.2.0", "displaydoc", "nom", "num-traits", @@ -747,7 +763,19 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "synstructure 0.13.1", ] [[package]] @@ -761,6 +789,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -2277,7 +2316,21 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ - "asn1-rs", + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs 0.6.1", "displaydoc", "nom", "num-bigint", @@ -6750,7 +6803,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -6966,6 +7019,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-format" version = "0.4.4" @@ -7077,7 +7136,16 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ - "asn1-rs", + "asn1-rs 0.5.2", +] + +[[package]] +name = "oid-registry" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" +dependencies = [ + "asn1-rs 0.6.1", ] [[package]] @@ -10608,6 +10676,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sp-certificate-registry" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-externalities", + "sp-runtime-interface", + "x509-parser 0.16.0", +] + [[package]] name = "sp-consensus" version = "0.10.0-dev" @@ -12377,6 +12456,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -12520,12 +12610,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -12540,10 +12631,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -13893,13 +13985,13 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ - "asn1-rs", + "asn1-rs 0.5.2", "base64 0.13.1", "data-encoding", - "der-parser", + "der-parser 8.2.0", "lazy_static", "nom", - "oid-registry", + "oid-registry 0.6.1", "rusticata-macros", "thiserror", "time", @@ -13911,12 +14003,30 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" dependencies = [ - "asn1-rs", + "asn1-rs 0.5.2", + "data-encoding", + "der-parser 8.2.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs 0.6.1", "data-encoding", - "der-parser", + "der-parser 9.0.0", "lazy_static", "nom", - "oid-registry", + "oid-registry 0.7.0", + "ring 0.17.7", "rusticata-macros", "thiserror", "time", diff --git a/domains/primitives/certificate-registry/Cargo.toml b/domains/primitives/certificate-registry/Cargo.toml new file mode 100644 index 0000000000..3179a271c3 --- /dev/null +++ b/domains/primitives/certificate-registry/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "sp-certificate-registry" +version = "0.1.0" +authors = ["Vedhavyas Singareddi "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://subspace.network" +repository = "https://github.com/subspace/subspace" +description = "Primitives for X509 Cerificate verification" +include = [ + "/src", + "/Cargo.toml", + "/README.md", +] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.5", default-features = false, features = ["derive"] } +scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } +sp-externalities = { version = "0.19.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +sp-runtime-interface = { version = "17.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +x509-parser = { version = "0.16.0", features = ["verify"], optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "sp-externalities/std", + "sp-runtime-interface/std", + "x509-parser" +] + +runtime-benchmarks = [] diff --git a/domains/primitives/certificate-registry/src/host_functions.rs b/domains/primitives/certificate-registry/src/host_functions.rs new file mode 100644 index 0000000000..4e2ca5d34b --- /dev/null +++ b/domains/primitives/certificate-registry/src/host_functions.rs @@ -0,0 +1,43 @@ +use crate::SignatureVerificationRequest; +use std::sync::Arc; +use x509_parser::der_parser::asn1_rs::BitString; +use x509_parser::prelude::{AlgorithmIdentifier, FromDer, SubjectPublicKeyInfo}; +use x509_parser::verify::verify_signature; + +/// Host function trait for Certificate registration +pub trait HostFunctions: Send + Sync { + fn verify_signature(&self, req: SignatureVerificationRequest) -> Option<()>; +} + +sp_externalities::decl_extension! { + pub struct HostFunctionExtension(Arc); +} + +impl HostFunctionExtension { + /// Create a new instance of [`HostFunctionExtension`]. + #[allow(dead_code)] + pub fn new(inner: Arc) -> Self { + Self(inner) + } +} + +/// Implementation of host functions for Certificate registry. +#[derive(Default)] +pub struct HostFunctionsImpl; + +impl HostFunctions for HostFunctionsImpl { + fn verify_signature(&self, req: SignatureVerificationRequest) -> Option<()> { + let SignatureVerificationRequest { + public_key_info, + signature_algorithm, + data, + signature, + } = req; + + let (_, public_key_info) = SubjectPublicKeyInfo::from_der(public_key_info.as_ref()).ok()?; + let (_, signature_algorithm) = + AlgorithmIdentifier::from_der(signature_algorithm.as_ref()).ok()?; + let signature = BitString::new(0, &signature); + verify_signature(&public_key_info, &signature_algorithm, &signature, &data).ok() + } +} diff --git a/domains/primitives/certificate-registry/src/lib.rs b/domains/primitives/certificate-registry/src/lib.rs new file mode 100644 index 0000000000..5c64fab539 --- /dev/null +++ b/domains/primitives/certificate-registry/src/lib.rs @@ -0,0 +1,66 @@ +// Copyright (C) 2021 Subspace Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Primitives for X509 certificate verification + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +mod host_functions; +mod runtime_interface; + +#[cfg(not(feature = "std"))] +extern crate alloc; + +pub use crate::runtime_interface::signature_verification_runtime_interface; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime_interface::pass_by; +use sp_runtime_interface::pass_by::PassBy; + +/// Signature verification request. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct SignatureVerificationRequest { + /// Der encoded public key info. + pub public_key_info: DerVec, + /// Der encoded signature algorithm. + pub signature_algorithm: DerVec, + /// Data that is being signed. + pub data: Vec, + /// Signature. + pub signature: Vec, +} + +impl PassBy for SignatureVerificationRequest { + type PassBy = pass_by::Codec; +} + +/// DER encoded bytes +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct DerVec(pub Vec); + +impl AsRef<[u8]> for DerVec { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From> for DerVec { + fn from(value: Vec) -> Self { + Self(value) + } +} diff --git a/domains/primitives/certificate-registry/src/runtime_interface.rs b/domains/primitives/certificate-registry/src/runtime_interface.rs new file mode 100644 index 0000000000..ac67e03707 --- /dev/null +++ b/domains/primitives/certificate-registry/src/runtime_interface.rs @@ -0,0 +1,19 @@ +#[cfg(feature = "std")] +use crate::host_functions::HostFunctionExtension; +use crate::SignatureVerificationRequest; +#[cfg(feature = "std")] +use sp_externalities::ExternalitiesExt; +use sp_runtime_interface::runtime_interface; + +/// Signature verification runtime interface. +#[runtime_interface] +pub trait SignatureVerificationRuntimeInterface { + #[allow(dead_code)] + fn verify_signature(&mut self, req: SignatureVerificationRequest) -> Option<()> { + self.extension::() + .expect( + "No `CertificateRegistryHostFunctionExtension` associated for the current context!", + ) + .verify_signature(req) + } +} From 971bb60a1422d2481d1849961caf18798d248095 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Fri, 22 Mar 2024 18:57:48 +0530 Subject: [PATCH 2/9] define pallet-auto-id and ability to register an auto id using X509 certificate --- Cargo.lock | 17 ++ domains/pallets/auto-id/Cargo.toml | 41 +++ domains/pallets/auto-id/src/lib.rs | 389 +++++++++++++++++++++++++++ domains/pallets/auto-id/src/tests.rs | 67 +++++ 4 files changed, 514 insertions(+) create mode 100644 domains/pallets/auto-id/Cargo.toml create mode 100644 domains/pallets/auto-id/src/lib.rs create mode 100644 domains/pallets/auto-id/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index e63637bfa1..306193d15d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7210,6 +7210,23 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "pallet-auto-id" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-certificate-registry", + "sp-core", + "sp-io", + "sp-runtime", + "subspace-runtime-primitives", + "x509-parser 0.16.0", +] + [[package]] name = "pallet-balances" version = "4.0.0-dev" diff --git a/domains/pallets/auto-id/Cargo.toml b/domains/pallets/auto-id/Cargo.toml new file mode 100644 index 0000000000..fa8e49a77a --- /dev/null +++ b/domains/pallets/auto-id/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "pallet-auto-id" +version = "0.1.0" +authors = ["Subspace Labs "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://subspace.network" +repository = "https://github.com/subspace/subspace" +description = "Auto ID pallet" +include = [ + "/src", + "/Cargo.toml", +] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.5", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } +sp-certificate-registry = { version = "0.1.0", default-features = false, path = "../../primitives/certificate-registry" } +sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +subspace-runtime-primitives = { version = "0.1.0", default-features = false, path = "../../../crates/subspace-runtime-primitives" } +x509-parser = { version = "0.16.0", default-features = false} + +[dev-dependencies] +pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +sp-io = { version = "23.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-certificate-registry/std", + "sp-core/std", + "sp-runtime/std", + "subspace-runtime-primitives/std", +] diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs new file mode 100644 index 0000000000..bedf6344d4 --- /dev/null +++ b/domains/pallets/auto-id/src/lib.rs @@ -0,0 +1,389 @@ +// Copyright (C) 2023 Subspace Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Pallet AutoID + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod tests; + +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::collections::BTreeSet; +use codec::{Decode, Encode}; +use frame_support::dispatch::DispatchResult; +use frame_support::traits::Time; +use frame_support::{ensure, PalletError}; +pub use pallet::*; +use scale_info::TypeInfo; +use sp_certificate_registry::signature_verification_runtime_interface::verify_signature; +use sp_certificate_registry::{DerVec, SignatureVerificationRequest}; +use sp_core::U256; +#[cfg(feature = "std")] +use std::collections::BTreeSet; +use subspace_runtime_primitives::Moment; +use x509_parser::certificate::TbsCertificate; +use x509_parser::prelude::FromDer; + +/// Unique AutoId identifier. +pub type Identifier = U256; + +/// Validity of a given certificate. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct Validity { + /// Not valid before the time since UNIX_EPOCH + pub not_before: Moment, + /// Not valid after the time since UNIX_EPOCH + pub not_after: Moment, +} + +impl Validity { + /// Checks if the certificate is valid at this time. + pub fn is_valid_at(&self, time: Moment) -> bool { + time >= self.not_before && time <= self.not_after + } +} + +/// Validity conversion error. +#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)] +pub enum ValidityError { + Overflow, + Expired, +} + +impl From for Error { + fn from(value: ValidityError) -> Self { + Error::::InvalidValidity(value) + } +} + +impl TryFrom for Validity { + type Error = ValidityError; + + fn try_from(value: x509_parser::certificate::Validity) -> Result { + Ok(Validity { + not_before: (value.not_before.timestamp() as u64) + .checked_mul(1000) + .and_then(|secs| { + secs.checked_add(value.not_before.to_datetime().millisecond() as u64) + }) + .ok_or(Self::Error::Overflow)?, + not_after: (value.not_after.timestamp() as u64) + .checked_mul(1000) + .and_then(|secs| { + secs.checked_add(value.not_after.to_datetime().millisecond() as u64) + }) + .ok_or(Self::Error::Overflow)?, + }) + } +} + +/// Root X509 Certificate. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct X509CertificateRoot { + /// Serial number for this certificate + pub serial: U256, + /// Der encoded certificate's subject. + pub subject: DerVec, + /// Der encoded certificate's subject's public key info + pub subject_pki: DerVec, + /// Validity of the certificate + pub validity: Validity, + /// Der encoded full X509 certificate. + pub raw: DerVec, + /// A list of all certificate serials issues by the subject. + /// Serial of root certificate is included as well. + pub issued_serials: BTreeSet, +} + +/// Leaf X509 certificate issued by a different issuer. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct X509CertificateLeaf { + /// Issuer identifier of this certificate. + pub issuer_id: Identifier, + /// Serial number for this certificate + pub serial: U256, + /// Der encoded certificate's subject. + pub subject: DerVec, + /// Der encoded certificate's subject's public key info + pub subject_pki: DerVec, + /// Validity of the certificate + pub validity: Validity, + /// Der encoded full X509 certificate. + pub raw: DerVec, +} + +/// An X509 certificate. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum X509Certificate { + /// A root X509 certificate. + Root(X509CertificateRoot), + /// A leaf certificate issued by a root certificate + Leaf(X509CertificateLeaf), +} + +/// Certificate associated with AutoId. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum Certificate { + X509(X509Certificate), +} + +impl Certificate { + /// Returns the public key info of a given root certificate. + fn x509_root_issuer_pki(&self) -> Option { + match self { + Certificate::X509(cert) => match cert { + X509Certificate::Root(cert) => Some(cert.subject_pki.clone()), + X509Certificate::Leaf(_) => None, + }, + } + } + + fn issue_x509_certificate_serial(&mut self, serial: U256) -> DispatchResult { + match self { + Certificate::X509(cert) => match cert { + X509Certificate::Root(cert) => { + ensure!( + !cert.issued_serials.contains(&serial), + Error::::CertificateSerialAlreadyIssued + ); + cert.issued_serials.insert(serial); + Ok(()) + } + // leaf cannot issue certs, return invalid + X509Certificate::Leaf(_) => Err(Error::::IssuerNotRoot.into()), + }, + } + } + + /// Checks if the certificate is valid at this time. + pub(crate) fn is_valid_at(&self, time: Moment) -> bool { + match self { + Certificate::X509(cert) => match cert { + X509Certificate::Root(cert) => cert.validity.is_valid_at(time), + X509Certificate::Leaf(cert) => cert.validity.is_valid_at(time), + }, + } + } +} + +/// A representation of AutoId +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct AutoId { + /// Unique AutoID identifier. + pub identifier: Identifier, + /// Certificate associated with this AutoId. + pub certificate: Certificate, +} + +/// Type holds X509 certificate details used to register an AutoId. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum RegisterAutoIdX509 { + Root { + certificate: DerVec, + signature_algorithm: DerVec, + signature: Vec, + }, + Leaf { + issuer_id: Identifier, + certificate: DerVec, + signature_algorithm: DerVec, + signature: Vec, + }, +} + +/// Request to register a new AutoId. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum RegisterAutoId { + X509(RegisterAutoIdX509), +} + +#[frame_support::pallet] +mod pallet { + use crate::{AutoId, Identifier, RegisterAutoId, ValidityError}; + use frame_support::pallet_prelude::*; + use frame_support::traits::Time; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type Time: Time; + } + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// Stores the next auto id identifier. + #[pallet::storage] + pub(super) type NextAutoIdIdentifier = StorageValue<_, Identifier, ValueQuery>; + + /// Stores the auto id identifier against an AutoId. + #[pallet::storage] + pub(super) type AutoIds = StorageMap<_, Identity, Identifier, AutoId, OptionQuery>; + + #[pallet::error] + pub enum Error { + /// Issuer auto id does not exist. + UnknownIssuer, + /// Issuer is not a root certificate. + IssuerNotRoot, + /// Certificate is invalid, + InvalidCertificate, + /// Invalid certificate validity. + InvalidValidity(ValidityError), + /// Invalid signature. + InvalidSignature, + /// AutoId identifier overflow. + IdentifierOverflow, + /// Certificate serial already issued. + CertificateSerialAlreadyIssued, + /// Certificate expired. + ExpiredCertificate, + } + + #[pallet::event] + #[pallet::generate_deposit(pub (super) fn deposit_event)] + pub enum Event { + NewAutoIdRegistered(Identifier), + } + + #[pallet::call] + impl Pallet { + /// Registers a new AutoId after validating the provided certificate. + #[pallet::call_index(0)] + // TODO: benchmark + #[pallet::weight({10_000})] + pub fn register_auto_id(origin: OriginFor, req: RegisterAutoId) -> DispatchResult { + ensure_signed(origin)?; + Self::do_register_auto_id(req)?; + Ok(()) + } + } +} + +impl Pallet { + #[allow(dead_code)] + pub(crate) fn do_register_auto_id(req: RegisterAutoId) -> DispatchResult { + let current_time = T::Time::now(); + let certificate = match req { + RegisterAutoId::X509(x509_req) => match x509_req { + RegisterAutoIdX509::Root { + certificate, + signature_algorithm, + signature, + } => { + let (_, tbs_certificate) = TbsCertificate::from_der(certificate.as_ref()) + .map_err(|_| Error::::InvalidCertificate)?; + + let req = SignatureVerificationRequest { + public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), + signature_algorithm, + data: certificate.0.clone(), + signature, + }; + verify_signature(req).ok_or(Error::::InvalidSignature)?; + + let serial = U256::from_big_endian(&tbs_certificate.serial.to_bytes_be()); + let validity = Validity::try_from(tbs_certificate.validity) + .map_err(Error::::InvalidValidity)?; + ensure!( + validity.is_valid_at(current_time), + Error::::InvalidValidity(ValidityError::Expired) + ); + + Certificate::X509(X509Certificate::Root(X509CertificateRoot { + serial, + subject: tbs_certificate.subject.as_raw().to_vec().into(), + subject_pki: tbs_certificate.subject_pki.raw.to_vec().into(), + validity, + raw: certificate, + issued_serials: BTreeSet::from([serial]), + })) + } + RegisterAutoIdX509::Leaf { + issuer_id, + certificate, + signature_algorithm, + signature, + } => { + let mut issuer_auto_id = + AutoIds::::get(issuer_id).ok_or(Error::::UnknownIssuer)?; + let issuer_pki = issuer_auto_id + .certificate + .x509_root_issuer_pki() + .ok_or(Error::::IssuerNotRoot)?; + + ensure!( + issuer_auto_id.certificate.is_valid_at(current_time), + Error::::ExpiredCertificate + ); + + let (_, tbs_certificate) = TbsCertificate::from_der(certificate.as_ref()) + .map_err(|_| Error::::InvalidCertificate)?; + + let req = SignatureVerificationRequest { + public_key_info: issuer_pki, + signature_algorithm, + data: certificate.0.clone(), + signature, + }; + verify_signature(req).ok_or(Error::::InvalidSignature)?; + let validity = Validity::try_from(tbs_certificate.validity) + .map_err(Error::::InvalidValidity)?; + ensure!( + validity.is_valid_at(current_time), + Error::::InvalidValidity(ValidityError::Expired) + ); + + let serial = U256::from_big_endian(&tbs_certificate.serial.to_bytes_be()); + issuer_auto_id + .certificate + .issue_x509_certificate_serial::(serial)?; + + AutoIds::::insert(issuer_id, issuer_auto_id); + + Certificate::X509(X509Certificate::Leaf(X509CertificateLeaf { + issuer_id, + serial, + subject: tbs_certificate.subject.as_raw().to_vec().into(), + subject_pki: tbs_certificate.subject_pki.raw.to_vec().into(), + validity, + raw: certificate, + })) + } + }, + }; + + let auto_id_identifier = NextAutoIdIdentifier::::get(); + let next_auto_id_identifier = auto_id_identifier + .checked_add(Identifier::one()) + .ok_or(Error::::IdentifierOverflow)?; + NextAutoIdIdentifier::::put(next_auto_id_identifier); + + let auto_id = AutoId { + identifier: auto_id_identifier, + certificate, + }; + + AutoIds::::insert(auto_id_identifier, auto_id); + + Self::deposit_event(Event::::NewAutoIdRegistered(auto_id_identifier)); + Ok(()) + } +} diff --git a/domains/pallets/auto-id/src/tests.rs b/domains/pallets/auto-id/src/tests.rs new file mode 100644 index 0000000000..a0bea30dae --- /dev/null +++ b/domains/pallets/auto-id/src/tests.rs @@ -0,0 +1,67 @@ +use crate::{self as pallet_auto_id}; +use frame_support::traits::{ConstU16, ConstU32, ConstU64}; +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; +use sp_runtime::BuildStorage; +use subspace_runtime_primitives::Moment; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub struct Test { + System: frame_system, + Timestamp: pallet_timestamp, + AutoId: pallet_auto_id, + } +); + +impl pallet_auto_id::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Time = Timestamp; +} + +const SLOT_DURATION: u64 = 1000; + +impl pallet_timestamp::Config for Test { + /// A timestamp: milliseconds since the unix epoch. + type Moment = Moment; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; + type WeightInfo = (); +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = RuntimeTask; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +#[allow(dead_code)] +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + t.into() +} From 246e754ded0602e0711e7b3b5de0eddb49b28254 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 25 Mar 2024 16:32:47 +0530 Subject: [PATCH 3/9] ability to revoke a certificate associated with AutoId and deactivate an AutoId --- domains/pallets/auto-id/src/lib.rs | 142 +++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs index bedf6344d4..66fbaa1acc 100644 --- a/domains/pallets/auto-id/src/lib.rs +++ b/domains/pallets/auto-id/src/lib.rs @@ -108,6 +108,8 @@ pub struct X509CertificateRoot { /// A list of all certificate serials issues by the subject. /// Serial of root certificate is included as well. pub issued_serials: BTreeSet, + /// Signifies if the certificate is revoked. + pub revoked: bool, } /// Leaf X509 certificate issued by a different issuer. @@ -125,6 +127,8 @@ pub struct X509CertificateLeaf { pub validity: Validity, /// Der encoded full X509 certificate. pub raw: DerVec, + /// Signifies if the certificate is revoked. + pub revoked: bool, } /// An X509 certificate. @@ -144,7 +148,7 @@ pub enum Certificate { impl Certificate { /// Returns the public key info of a given root certificate. - fn x509_root_issuer_pki(&self) -> Option { + fn root_issuer_pki(&self) -> Option { match self { Certificate::X509(cert) => match cert { X509Certificate::Root(cert) => Some(cert.subject_pki.clone()), @@ -153,7 +157,17 @@ impl Certificate { } } - fn issue_x509_certificate_serial(&mut self, serial: U256) -> DispatchResult { + /// Returns the subject public key info. + fn subject_pki(&self) -> DerVec { + match self { + Certificate::X509(cert) => match cert { + X509Certificate::Root(cert) => cert.subject_pki.clone(), + X509Certificate::Leaf(cert) => cert.subject_pki.clone(), + }, + } + } + + fn issue_certificate_serial(&mut self, serial: U256) -> DispatchResult { match self { Certificate::X509(cert) => match cert { X509Certificate::Root(cert) => { @@ -179,6 +193,24 @@ impl Certificate { }, } } + + fn revoke(&mut self) { + match self { + Certificate::X509(cert) => match cert { + X509Certificate::Root(cert) => cert.revoked = true, + X509Certificate::Leaf(cert) => cert.revoked = true, + }, + } + } + + fn is_revoked(&self) -> bool { + match self { + Certificate::X509(cert) => match cert { + X509Certificate::Root(cert) => cert.revoked, + X509Certificate::Leaf(cert) => cert.revoked, + }, + } + } } /// A representation of AutoId @@ -206,6 +238,13 @@ pub enum RegisterAutoIdX509 { }, } +/// Signature holds algorithm used and the signature value. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct Signature { + pub signature_algorithm: DerVec, + pub value: Vec, +} + /// Request to register a new AutoId. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub enum RegisterAutoId { @@ -214,7 +253,7 @@ pub enum RegisterAutoId { #[frame_support::pallet] mod pallet { - use crate::{AutoId, Identifier, RegisterAutoId, ValidityError}; + use crate::{AutoId, Identifier, RegisterAutoId, Signature, ValidityError}; use frame_support::pallet_prelude::*; use frame_support::traits::Time; use frame_system::pallet_prelude::*; @@ -255,12 +294,19 @@ mod pallet { CertificateSerialAlreadyIssued, /// Certificate expired. ExpiredCertificate, + /// Certificate revoked. + CertificateRevoked, } #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { + /// Emits when a new AutoId is registered. NewAutoIdRegistered(Identifier), + /// Emits when a Certificate associated with AutoId is revoked. + CertificateRevoked(Identifier), + /// Emits when an AutoId is deactivated. + AutoIdDeactivated(Identifier), } #[pallet::call] @@ -274,11 +320,38 @@ mod pallet { Self::do_register_auto_id(req)?; Ok(()) } + + /// Revokes a certificate associated with given AutoId. + #[pallet::call_index(1)] + // TODO: benchmark + #[pallet::weight({10_000})] + pub fn revoke_certificate( + origin: OriginFor, + auto_id_identifier: Identifier, + signature: Signature, + ) -> DispatchResult { + ensure_signed(origin)?; + Self::do_revoke_certificate(auto_id_identifier, signature)?; + Ok(()) + } + + /// Deactivates a given AutoId. + #[pallet::call_index(2)] + // TODO: benchmark + #[pallet::weight({10_000})] + pub fn deactivate_auto_id( + origin: OriginFor, + auto_id_identifier: Identifier, + signature: Signature, + ) -> DispatchResult { + ensure_signed(origin)?; + Self::do_deactivate_auto_id(auto_id_identifier, signature)?; + Ok(()) + } } } impl Pallet { - #[allow(dead_code)] pub(crate) fn do_register_auto_id(req: RegisterAutoId) -> DispatchResult { let current_time = T::Time::now(); let certificate = match req { @@ -314,6 +387,7 @@ impl Pallet { validity, raw: certificate, issued_serials: BTreeSet::from([serial]), + revoked: false, })) } RegisterAutoIdX509::Leaf { @@ -326,7 +400,7 @@ impl Pallet { AutoIds::::get(issuer_id).ok_or(Error::::UnknownIssuer)?; let issuer_pki = issuer_auto_id .certificate - .x509_root_issuer_pki() + .root_issuer_pki() .ok_or(Error::::IssuerNotRoot)?; ensure!( @@ -334,6 +408,11 @@ impl Pallet { Error::::ExpiredCertificate ); + ensure!( + !issuer_auto_id.certificate.is_revoked(), + Error::::CertificateRevoked + ); + let (_, tbs_certificate) = TbsCertificate::from_der(certificate.as_ref()) .map_err(|_| Error::::InvalidCertificate)?; @@ -354,7 +433,7 @@ impl Pallet { let serial = U256::from_big_endian(&tbs_certificate.serial.to_bytes_be()); issuer_auto_id .certificate - .issue_x509_certificate_serial::(serial)?; + .issue_certificate_serial::(serial)?; AutoIds::::insert(issuer_id, issuer_auto_id); @@ -365,6 +444,7 @@ impl Pallet { subject_pki: tbs_certificate.subject_pki.raw.to_vec().into(), validity, raw: certificate, + revoked: false, })) } }, @@ -386,4 +466,54 @@ impl Pallet { Self::deposit_event(Event::::NewAutoIdRegistered(auto_id_identifier)); Ok(()) } + + fn do_verify_signature(auto_id: &AutoId, signature: Signature) -> DispatchResult { + let Signature { + signature_algorithm, + value: signature, + } = signature; + let req = SignatureVerificationRequest { + public_key_info: auto_id.certificate.subject_pki(), + signature_algorithm, + // uses auto_id identifier as the message to sign + data: auto_id.identifier.encode(), + signature, + }; + + verify_signature(req).ok_or(Error::::InvalidSignature)?; + Ok(()) + } + + fn do_revoke_certificate( + auto_id_identifier: Identifier, + signature: Signature, + ) -> DispatchResult { + let mut auto_id = AutoIds::::get(auto_id_identifier).ok_or(Error::::UnknownIssuer)?; + Self::do_verify_signature(&auto_id, signature)?; + ensure!( + !auto_id.certificate.is_revoked(), + Error::::CertificateRevoked + ); + + auto_id.certificate.revoke(); + // TODO: revoke all the issued leaf certificates if this is an issuer certificate. + AutoIds::::insert(auto_id_identifier, auto_id); + + Self::deposit_event(Event::::CertificateRevoked(auto_id_identifier)); + Ok(()) + } + + fn do_deactivate_auto_id( + auto_id_identifier: Identifier, + signature: Signature, + ) -> DispatchResult { + let auto_id = AutoIds::::get(auto_id_identifier).ok_or(Error::::UnknownIssuer)?; + Self::do_verify_signature(&auto_id, signature)?; + + // TODO: remove all the AutoIds registered using leaf certificates if this is the issuer. + AutoIds::::remove(auto_id_identifier); + + Self::deposit_event(Event::::AutoIdDeactivated(auto_id_identifier)); + Ok(()) + } } From a11c528a0bd83e63f37bd3c88aea167d376fb4ce Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 25 Mar 2024 20:29:55 +0530 Subject: [PATCH 4/9] add test cases to verify certificate and signature on pallet --- .gitguardian.yaml | 6 + Cargo.lock | 26 +-- domains/pallets/auto-id/Cargo.toml | 3 +- domains/pallets/auto-id/res/issuer.cert.der | Bin 0 -> 981 bytes domains/pallets/auto-id/res/leaf.cert.der | Bin 0 -> 1009 bytes .../pallets/auto-id/res/private.issuer.pem | 28 +++ domains/pallets/auto-id/src/tests.rs | 193 ++++++++++++++++-- .../src/host_functions.rs | 1 - .../certificate-registry/src/lib.rs | 2 +- .../src/runtime_interface.rs | 1 - 10 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 .gitguardian.yaml create mode 100644 domains/pallets/auto-id/res/issuer.cert.der create mode 100644 domains/pallets/auto-id/res/leaf.cert.der create mode 100644 domains/pallets/auto-id/res/private.issuer.pem diff --git a/.gitguardian.yaml b/.gitguardian.yaml new file mode 100644 index 0000000000..0c5d8b6e78 --- /dev/null +++ b/.gitguardian.yaml @@ -0,0 +1,6 @@ +version: 2 +iac: + ignored-paths: + - path: 'domains/pallets/auto-id/res/*' + comment: 'Ignore secrets used for testing' + diff --git a/Cargo.lock b/Cargo.lock index 306193d15d..456a47a79d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7216,8 +7216,9 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", - "pallet-timestamp", "parity-scale-codec", + "pem 3.0.3", + "ring 0.17.8", "scale-info", "sp-certificate-registry", "sp-core", @@ -8739,16 +8740,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom 0.2.12", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -8940,7 +8942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] @@ -8952,7 +8954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.2", "subtle 2.5.0", @@ -8992,7 +8994,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -9002,7 +9004,7 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "rustls-pki-types", "untrusted 0.9.0", ] @@ -10191,7 +10193,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -10530,7 +10532,7 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek 4.1.2", "rand_core 0.6.4", - "ring 0.17.7", + "ring 0.17.8", "rustc_version", "sha2 0.10.8", "subtle 2.5.0", @@ -13632,7 +13634,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -14043,7 +14045,7 @@ dependencies = [ "lazy_static", "nom", "oid-registry 0.7.0", - "ring 0.17.7", + "ring 0.17.8", "rusticata-macros", "thiserror", "time", diff --git a/domains/pallets/auto-id/Cargo.toml b/domains/pallets/auto-id/Cargo.toml index fa8e49a77a..3fed0aa09f 100644 --- a/domains/pallets/auto-id/Cargo.toml +++ b/domains/pallets/auto-id/Cargo.toml @@ -24,7 +24,8 @@ subspace-runtime-primitives = { version = "0.1.0", default-features = false, pat x509-parser = { version = "0.16.0", default-features = false} [dev-dependencies] -pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +pem = "3.0.3" +ring = "0.17.8" sp-io = { version = "23.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } [features] diff --git a/domains/pallets/auto-id/res/issuer.cert.der b/domains/pallets/auto-id/res/issuer.cert.der new file mode 100644 index 0000000000000000000000000000000000000000..c10a4217f5ae0c9b4bf12c334e4f3f6fa3e692c6 GIT binary patch literal 981 zcmXqLV!mk5#I$n(GZP~dlL*_}uAi%nzQ>wqzJ6wRSHZJOsovOtmyJ`a&7XDOfke1@xLxzoGxJhYi!$@l4P_0aL88n&B4Md18Hr_;iNy-RK#9bn)RdG=LwN&P zxDz-T#RM`k9m-(p^iopG4CKUljZ6%TjSLKpjg3r9qJUgeB<^5zaucHxa=*0AF)}hNmJk+>Ol$YlQ+qA{zBw#rf{>bo!iSLLbsAIq_$E$~x+8o2cI7th zfU~K3Z^S>D%%AeSsBV3=hWMLrv+sZEnIC?Jy~A~th|!F1|0l@rIMq_UD?YC1O-p9` zuWq*HIm}vhr!q>O{VgFfclzwWTgAWUh5V>KZv0SeCTDTIgigbS{9l~!7j;d(Ev}!n?x}(& z!;h0G|6cbo8_(dCsi;^tFZN62V3eVyIUJyJ6Rr^W|JaZzxfzfK)>$ff1fVeAIzMvXw{s$^Q>2cwy*8f zK6sCduY`5m^!;}wHSfn)*k(U``MO}W`-&BLAH0iRW*Tk`ZTDQK{!YR_)aNY!`U4lA z6vvePyUV#NFcp h=e;QD={3uJ7oIv^X)XRgU%l!4S!3?)&F;&J@m>`LOa`$(xG?ylk9WZ60mkc^MhG zSs4r(Cm3=YaI!Invaks=dHTUP96U@R!G=5rTp$^C9%he77efI9J`j(Khuys>zq}+P z!cfFO2qePI!|j@vo|%`LT9lcWZYXOY4H9MM5eZ98$w(}#Oe|Ii21+CrrKY508p<2U z!kxg$C?=4R=}-n!ra6??p671~mgTu*u20Rcavj*hnXC)eH8mJ??QK|qW3UYp>USf83 zYOX_iZenJRUUGh}K@+1Aa+or*GB7tW@-qO%xtN+585yS9P8Zx{FR!_h)3)RED}jF* zs8{w%%hb86*Zi3vY8+T$VVBis|YAZ&l! znY)2aC6aIZ)}?6tUG&Oce46Knh%DA$S!I%$aeCQYW?h%(`&QVBH?Lcs8O_t*waUk1 zWs$M@s>RH|WwN3^y`OZq)YDf!WXXYRxBgsnaawns;kK2Ia^9}LADj2{UrU^L`S%-F zslMyZf9vZ`wJ=P3nBM{&RP@rc03?0;Q<_rx`4Z~A9{#x#(_ zw)>XDdZS`Ft_P{n-*OEvZC{{jCpYs#R$iibT>pf|1i!h@r*%zIT36otX63S!yw(0U z*4_PYld--ydP&5Y)zfrWMw>6Xvc|Um?}RTUN@BTc{@RkueXE$xin1tkU1wqt_%^L{ zvq_kxqLchOxpyz@9F}xV^IL2k?$FU0RV~M8Fp;VEjbNAH>q$1%o;NOAE#4D+@Xg&V z4qd-J^WRASuKPCo>Z$n`LRcb1pSwA_yDa8lv6wfnUvKT}YnxNjSJubB$-U{EpvSs7 h=*XjsO3zM7_=s}Z$kct`)RQ9D8`9vK;dy; frame_support::construct_runtime!( pub struct Test { System: frame_system, - Timestamp: pallet_timestamp, AutoId: pallet_auto_id, } ); -impl pallet_auto_id::Config for Test { - type RuntimeEvent = RuntimeEvent; - type Time = Timestamp; -} +pub struct MockTime; +impl Time for MockTime { + type Moment = Moment; -const SLOT_DURATION: u64 = 1000; + fn now() -> Self::Moment { + // valid block time for testing certs + 1_711_367_658_200 + } +} -impl pallet_timestamp::Config for Test { - /// A timestamp: milliseconds since the unix epoch. - type Moment = Moment; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; - type WeightInfo = (); +impl pallet_auto_id::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Time = MockTime; } impl frame_system::Config for Test { @@ -57,11 +69,160 @@ impl frame_system::Config for Test { type MaxConsumers = ConstU32<16>; } -#[allow(dead_code)] pub(crate) fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default() .build_storage() .unwrap(); - t.into() + let mut ext: sp_io::TestExternalities = t.into(); + ext.register_extension( + sp_certificate_registry::host_functions::HostFunctionExtension::new(Arc::new( + sp_certificate_registry::host_functions::HostFunctionsImpl, + )), + ); + ext +} + +/// Converts Algorithm identifier to Der since x509 does not implement the ToDer :(. +fn algorithm_to_der(algorithm_identifier: AlgorithmIdentifier) -> DerVec { + let sequence_tag: u8 = 0x30; + let sequence_content = { + let mut temp = Vec::new(); + temp.extend(algorithm_identifier.algorithm.to_der_vec().unwrap()); + temp.extend(algorithm_identifier.parameters.to_der_vec().unwrap()); + temp + }; + let encoded_sequence_length = { + let content_length = sequence_content.len(); + if content_length > 127 { + // This is long form length encoding + let length_as_bytes = content_length.to_be_bytes(); + let mut encoded = Vec::with_capacity(length_as_bytes.len() + 1); + // Set first bit to 1 and store number of length-bytes. + encoded.push(0x80 | (length_as_bytes.len() as u8)); + encoded.extend_from_slice(&length_as_bytes); + encoded + } else { + // The short form (single-byte length) can be used. + vec![content_length as u8] + } + }; + + let mut d = Vec::new(); + d.push(sequence_tag); + d.extend(encoded_sequence_length); + d.extend(sequence_content); + let (_, derived) = AlgorithmIdentifier::from_der(&d).unwrap(); + assert_eq!(algorithm_identifier, derived); + d.into() +} + +fn register_issuer_auto_id() -> Identifier { + let issuer_cert = include_bytes!("../res/issuer.cert.der").to_vec(); + let (_, cert) = x509_parser::certificate::X509Certificate::from_der(&issuer_cert).unwrap(); + + let auto_id_identifier = NextAutoIdIdentifier::::get(); + Pallet::::register_auto_id( + RawOrigin::Signed(1).into(), + RegisterAutoId::X509(RegisterAutoIdX509::Root { + certificate: cert.tbs_certificate.as_ref().to_vec().into(), + signature_algorithm: algorithm_to_der(cert.signature_algorithm), + signature: cert.signature_value.as_ref().to_vec(), + }), + ) + .unwrap(); + + assert_eq!(NextAutoIdIdentifier::::get(), auto_id_identifier + 1); + auto_id_identifier +} + +fn register_leaf_auto_id(issuer_auto_id: Identifier) -> Identifier { + let cert = include_bytes!("../res/leaf.cert.der").to_vec(); + let (_, cert) = x509_parser::certificate::X509Certificate::from_der(&cert).unwrap(); + let auto_id_identifier = NextAutoIdIdentifier::::get(); + Pallet::::register_auto_id( + RawOrigin::Signed(1).into(), + RegisterAutoId::X509(RegisterAutoIdX509::Leaf { + issuer_id: issuer_auto_id, + certificate: cert.tbs_certificate.as_ref().to_vec().into(), + signature_algorithm: algorithm_to_der(cert.signature_algorithm), + signature: cert.signature_value.as_ref().to_vec(), + }), + ) + .unwrap(); + + assert_eq!(NextAutoIdIdentifier::::get(), auto_id_identifier + 1); + auto_id_identifier +} + +fn sign_preimage(data: Vec) -> Signature { + let priv_key_pem = include_str!("../res/private.issuer.pem"); + let priv_key_der = parse(priv_key_pem).unwrap().contents().to_vec(); + let rsa_key_pair = RsaKeyPair::from_pkcs8(&priv_key_der).unwrap(); + let mut signature = vec![0; rsa_key_pair.public().modulus_len()]; + let rng = SystemRandom::new(); + rsa_key_pair + .sign( + &ring::signature::RSA_PKCS1_SHA256, + &rng, + &data, + &mut signature, + ) + .unwrap(); + let algo = AlgorithmIdentifier { + algorithm: OID_PKCS1_SHA256WITHRSA, + parameters: None, + }; + Signature { + signature_algorithm: algorithm_to_der(algo), + value: signature, + } +} + +#[test] +fn test_register_issuer_auto_id() { + new_test_ext().execute_with(|| { + register_issuer_auto_id(); + }) +} + +#[test] +fn test_register_leaf_auto_id() { + new_test_ext().execute_with(|| { + let issuer_id = register_issuer_auto_id(); + register_leaf_auto_id(issuer_id); + }) +} + +#[test] +fn test_revoke_certificate() { + new_test_ext().execute_with(|| { + let auto_id_identifier = register_issuer_auto_id(); + let auto_id = AutoIds::::get(auto_id_identifier).unwrap(); + assert!(!auto_id.certificate.is_revoked()); + let signature = sign_preimage(auto_id_identifier.encode()); + Pallet::::revoke_certificate( + RawOrigin::Signed(1).into(), + auto_id_identifier, + signature, + ) + .unwrap(); + let auto_id = AutoIds::::get(auto_id_identifier).unwrap(); + assert!(auto_id.certificate.is_revoked()); + }) +} + +#[test] +fn test_deactivate_auto_id() { + new_test_ext().execute_with(|| { + let auto_id_identifier = register_issuer_auto_id(); + let signature = sign_preimage(auto_id_identifier.encode()); + Pallet::::deactivate_auto_id( + RawOrigin::Signed(1).into(), + auto_id_identifier, + signature, + ) + .unwrap(); + assert!(AutoIds::::get(auto_id_identifier).is_none()); + }) } diff --git a/domains/primitives/certificate-registry/src/host_functions.rs b/domains/primitives/certificate-registry/src/host_functions.rs index 4e2ca5d34b..b0ab7758ef 100644 --- a/domains/primitives/certificate-registry/src/host_functions.rs +++ b/domains/primitives/certificate-registry/src/host_functions.rs @@ -15,7 +15,6 @@ sp_externalities::decl_extension! { impl HostFunctionExtension { /// Create a new instance of [`HostFunctionExtension`]. - #[allow(dead_code)] pub fn new(inner: Arc) -> Self { Self(inner) } diff --git a/domains/primitives/certificate-registry/src/lib.rs b/domains/primitives/certificate-registry/src/lib.rs index 5c64fab539..adfc1eb53d 100644 --- a/domains/primitives/certificate-registry/src/lib.rs +++ b/domains/primitives/certificate-registry/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "std")] -mod host_functions; +pub mod host_functions; mod runtime_interface; #[cfg(not(feature = "std"))] diff --git a/domains/primitives/certificate-registry/src/runtime_interface.rs b/domains/primitives/certificate-registry/src/runtime_interface.rs index ac67e03707..25882cdd4d 100644 --- a/domains/primitives/certificate-registry/src/runtime_interface.rs +++ b/domains/primitives/certificate-registry/src/runtime_interface.rs @@ -8,7 +8,6 @@ use sp_runtime_interface::runtime_interface; /// Signature verification runtime interface. #[runtime_interface] pub trait SignatureVerificationRuntimeInterface { - #[allow(dead_code)] fn verify_signature(&mut self, req: SignatureVerificationRequest) -> Option<()> { self.extension::() .expect( From f005d6344c58d92cef5a58655262bffa4228cb45 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 28 Mar 2024 10:47:20 +0530 Subject: [PATCH 5/9] simplify the X509 struct and rename subject_pki with full word to avoid confusion --- domains/pallets/auto-id/src/lib.rs | 113 ++++++++--------------------- 1 file changed, 29 insertions(+), 84 deletions(-) diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs index 66fbaa1acc..460f2a6750 100644 --- a/domains/pallets/auto-id/src/lib.rs +++ b/domains/pallets/auto-id/src/lib.rs @@ -92,15 +92,17 @@ impl TryFrom for Validity { } } -/// Root X509 Certificate. +/// X509 certificate. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] -pub struct X509CertificateRoot { +pub struct X509Certificate { + /// Issuer identifier of this certificate. + pub issuer_id: Option, /// Serial number for this certificate pub serial: U256, /// Der encoded certificate's subject. pub subject: DerVec, /// Der encoded certificate's subject's public key info - pub subject_pki: DerVec, + pub subject_public_key_info: DerVec, /// Validity of the certificate pub validity: Validity, /// Der encoded full X509 certificate. @@ -112,34 +114,6 @@ pub struct X509CertificateRoot { pub revoked: bool, } -/// Leaf X509 certificate issued by a different issuer. -#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] -pub struct X509CertificateLeaf { - /// Issuer identifier of this certificate. - pub issuer_id: Identifier, - /// Serial number for this certificate - pub serial: U256, - /// Der encoded certificate's subject. - pub subject: DerVec, - /// Der encoded certificate's subject's public key info - pub subject_pki: DerVec, - /// Validity of the certificate - pub validity: Validity, - /// Der encoded full X509 certificate. - pub raw: DerVec, - /// Signifies if the certificate is revoked. - pub revoked: bool, -} - -/// An X509 certificate. -#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] -pub enum X509Certificate { - /// A root X509 certificate. - Root(X509CertificateRoot), - /// A leaf certificate issued by a root certificate - Leaf(X509CertificateLeaf), -} - /// Certificate associated with AutoId. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub enum Certificate { @@ -147,68 +121,42 @@ pub enum Certificate { } impl Certificate { - /// Returns the public key info of a given root certificate. - fn root_issuer_pki(&self) -> Option { - match self { - Certificate::X509(cert) => match cert { - X509Certificate::Root(cert) => Some(cert.subject_pki.clone()), - X509Certificate::Leaf(_) => None, - }, - } - } - /// Returns the subject public key info. - fn subject_pki(&self) -> DerVec { + fn subject_public_key_info(&self) -> DerVec { match self { - Certificate::X509(cert) => match cert { - X509Certificate::Root(cert) => cert.subject_pki.clone(), - X509Certificate::Leaf(cert) => cert.subject_pki.clone(), - }, + Certificate::X509(cert) => cert.subject_public_key_info.clone(), } } fn issue_certificate_serial(&mut self, serial: U256) -> DispatchResult { match self { - Certificate::X509(cert) => match cert { - X509Certificate::Root(cert) => { - ensure!( - !cert.issued_serials.contains(&serial), - Error::::CertificateSerialAlreadyIssued - ); - cert.issued_serials.insert(serial); - Ok(()) - } - // leaf cannot issue certs, return invalid - X509Certificate::Leaf(_) => Err(Error::::IssuerNotRoot.into()), - }, + Certificate::X509(cert) => { + ensure!( + !cert.issued_serials.contains(&serial), + Error::::CertificateSerialAlreadyIssued + ); + cert.issued_serials.insert(serial); + Ok(()) + } } } /// Checks if the certificate is valid at this time. pub(crate) fn is_valid_at(&self, time: Moment) -> bool { match self { - Certificate::X509(cert) => match cert { - X509Certificate::Root(cert) => cert.validity.is_valid_at(time), - X509Certificate::Leaf(cert) => cert.validity.is_valid_at(time), - }, + Certificate::X509(cert) => cert.validity.is_valid_at(time), } } fn revoke(&mut self) { match self { - Certificate::X509(cert) => match cert { - X509Certificate::Root(cert) => cert.revoked = true, - X509Certificate::Leaf(cert) => cert.revoked = true, - }, + Certificate::X509(cert) => cert.revoked = true, } } fn is_revoked(&self) -> bool { match self { - Certificate::X509(cert) => match cert { - X509Certificate::Root(cert) => cert.revoked, - X509Certificate::Leaf(cert) => cert.revoked, - }, + Certificate::X509(cert) => cert.revoked, } } } @@ -280,8 +228,6 @@ mod pallet { pub enum Error { /// Issuer auto id does not exist. UnknownIssuer, - /// Issuer is not a root certificate. - IssuerNotRoot, /// Certificate is invalid, InvalidCertificate, /// Invalid certificate validity. @@ -380,15 +326,16 @@ impl Pallet { Error::::InvalidValidity(ValidityError::Expired) ); - Certificate::X509(X509Certificate::Root(X509CertificateRoot { + Certificate::X509(X509Certificate { + issuer_id: None, serial, subject: tbs_certificate.subject.as_raw().to_vec().into(), - subject_pki: tbs_certificate.subject_pki.raw.to_vec().into(), + subject_public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), validity, raw: certificate, issued_serials: BTreeSet::from([serial]), revoked: false, - })) + }) } RegisterAutoIdX509::Leaf { issuer_id, @@ -398,10 +345,7 @@ impl Pallet { } => { let mut issuer_auto_id = AutoIds::::get(issuer_id).ok_or(Error::::UnknownIssuer)?; - let issuer_pki = issuer_auto_id - .certificate - .root_issuer_pki() - .ok_or(Error::::IssuerNotRoot)?; + let issuer_pki = issuer_auto_id.certificate.subject_public_key_info(); ensure!( issuer_auto_id.certificate.is_valid_at(current_time), @@ -437,15 +381,16 @@ impl Pallet { AutoIds::::insert(issuer_id, issuer_auto_id); - Certificate::X509(X509Certificate::Leaf(X509CertificateLeaf { - issuer_id, + Certificate::X509(X509Certificate { + issuer_id: Some(issuer_id), serial, subject: tbs_certificate.subject.as_raw().to_vec().into(), - subject_pki: tbs_certificate.subject_pki.raw.to_vec().into(), + subject_public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), validity, raw: certificate, + issued_serials: BTreeSet::from([serial]), revoked: false, - })) + }) } }, }; @@ -473,7 +418,7 @@ impl Pallet { value: signature, } = signature; let req = SignatureVerificationRequest { - public_key_info: auto_id.certificate.subject_pki(), + public_key_info: auto_id.certificate.subject_public_key_info(), signature_algorithm, // uses auto_id identifier as the message to sign data: auto_id.identifier.encode(), From d875a5aac35c1732eec61ce4359782eff58ff6fc Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 28 Mar 2024 11:23:00 +0530 Subject: [PATCH 6/9] rename crate sp-certificate-registry to sp-auto-id --- Cargo.lock | 24 +++++++++---------- domains/pallets/auto-id/Cargo.toml | 4 ++-- domains/pallets/auto-id/src/lib.rs | 4 ++-- domains/pallets/auto-id/src/tests.rs | 10 ++++---- .../Cargo.toml | 4 ++-- .../src/host_functions.rs | 0 .../src/lib.rs | 0 .../src/runtime_interface.rs | 0 8 files changed, 22 insertions(+), 24 deletions(-) rename domains/primitives/{certificate-registry => auto-id}/Cargo.toml (92%) rename domains/primitives/{certificate-registry => auto-id}/src/host_functions.rs (100%) rename domains/primitives/{certificate-registry => auto-id}/src/lib.rs (100%) rename domains/primitives/{certificate-registry => auto-id}/src/runtime_interface.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 456a47a79d..126ab85dee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7220,7 +7220,7 @@ dependencies = [ "pem 3.0.3", "ring 0.17.8", "scale-info", - "sp-certificate-registry", + "sp-auto-id", "sp-core", "sp-io", "sp-runtime", @@ -10655,6 +10655,17 @@ dependencies = [ "sp-crypto-ec-utils", ] +[[package]] +name = "sp-auto-id" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-externalities", + "sp-runtime-interface", + "x509-parser 0.16.0", +] + [[package]] name = "sp-block-builder" version = "4.0.0-dev" @@ -10695,17 +10706,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sp-certificate-registry" -version = "0.1.0" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-externalities", - "sp-runtime-interface", - "x509-parser 0.16.0", -] - [[package]] name = "sp-consensus" version = "0.10.0-dev" diff --git a/domains/pallets/auto-id/Cargo.toml b/domains/pallets/auto-id/Cargo.toml index 3fed0aa09f..7bd3125425 100644 --- a/domains/pallets/auto-id/Cargo.toml +++ b/domains/pallets/auto-id/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.6.5", default-features = frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } -sp-certificate-registry = { version = "0.1.0", default-features = false, path = "../../primitives/certificate-registry" } +sp-auto-id = { version = "0.1.0", default-features = false, path = "../../primitives/auto-id" } sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } subspace-runtime-primitives = { version = "0.1.0", default-features = false, path = "../../../crates/subspace-runtime-primitives" } @@ -35,7 +35,7 @@ std = [ "frame-support/std", "frame-system/std", "scale-info/std", - "sp-certificate-registry/std", + "sp-auto-id/std", "sp-core/std", "sp-runtime/std", "subspace-runtime-primitives/std", diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs index 460f2a6750..32ec3dd433 100644 --- a/domains/pallets/auto-id/src/lib.rs +++ b/domains/pallets/auto-id/src/lib.rs @@ -30,8 +30,8 @@ use frame_support::traits::Time; use frame_support::{ensure, PalletError}; pub use pallet::*; use scale_info::TypeInfo; -use sp_certificate_registry::signature_verification_runtime_interface::verify_signature; -use sp_certificate_registry::{DerVec, SignatureVerificationRequest}; +use sp_auto_id::signature_verification_runtime_interface::verify_signature; +use sp_auto_id::{DerVec, SignatureVerificationRequest}; use sp_core::U256; #[cfg(feature = "std")] use std::collections::BTreeSet; diff --git a/domains/pallets/auto-id/src/tests.rs b/domains/pallets/auto-id/src/tests.rs index 6c6389d26d..4088f1f1e7 100644 --- a/domains/pallets/auto-id/src/tests.rs +++ b/domains/pallets/auto-id/src/tests.rs @@ -8,7 +8,7 @@ use frame_support::traits::{ConstU16, ConstU32, ConstU64, Time}; use pem::parse; use ring::rand::SystemRandom; use ring::signature::RsaKeyPair; -use sp_certificate_registry::DerVec; +use sp_auto_id::DerVec; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use sp_runtime::BuildStorage; @@ -75,11 +75,9 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities { .unwrap(); let mut ext: sp_io::TestExternalities = t.into(); - ext.register_extension( - sp_certificate_registry::host_functions::HostFunctionExtension::new(Arc::new( - sp_certificate_registry::host_functions::HostFunctionsImpl, - )), - ); + ext.register_extension(sp_auto_id::host_functions::HostFunctionExtension::new( + Arc::new(sp_auto_id::host_functions::HostFunctionsImpl), + )); ext } diff --git a/domains/primitives/certificate-registry/Cargo.toml b/domains/primitives/auto-id/Cargo.toml similarity index 92% rename from domains/primitives/certificate-registry/Cargo.toml rename to domains/primitives/auto-id/Cargo.toml index 3179a271c3..e621b0ed08 100644 --- a/domains/primitives/certificate-registry/Cargo.toml +++ b/domains/primitives/auto-id/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "sp-certificate-registry" +name = "sp-auto-id" version = "0.1.0" authors = ["Vedhavyas Singareddi "] edition = "2021" license = "Apache-2.0" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" -description = "Primitives for X509 Cerificate verification" +description = "Primitives for AutoId" include = [ "/src", "/Cargo.toml", diff --git a/domains/primitives/certificate-registry/src/host_functions.rs b/domains/primitives/auto-id/src/host_functions.rs similarity index 100% rename from domains/primitives/certificate-registry/src/host_functions.rs rename to domains/primitives/auto-id/src/host_functions.rs diff --git a/domains/primitives/certificate-registry/src/lib.rs b/domains/primitives/auto-id/src/lib.rs similarity index 100% rename from domains/primitives/certificate-registry/src/lib.rs rename to domains/primitives/auto-id/src/lib.rs diff --git a/domains/primitives/certificate-registry/src/runtime_interface.rs b/domains/primitives/auto-id/src/runtime_interface.rs similarity index 100% rename from domains/primitives/certificate-registry/src/runtime_interface.rs rename to domains/primitives/auto-id/src/runtime_interface.rs From 057515ef5d8111c4de417692a0394c4a7f4331c7 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Fri, 29 Mar 2024 10:54:40 +0530 Subject: [PATCH 7/9] move tbs certificate decoding to host function since x509_parser is not compilable in no_std --- Cargo.lock | 2 + domains/pallets/auto-id/Cargo.toml | 2 +- domains/pallets/auto-id/src/lib.rs | 117 +++++------------- domains/primitives/auto-id/Cargo.toml | 4 + .../primitives/auto-id/src/host_functions.rs | 17 ++- domains/primitives/auto-id/src/lib.rs | 64 ++++++++++ .../auto-id/src/runtime_interface.rs | 10 +- 7 files changed, 127 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 126ab85dee..2c49cf5a5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10661,8 +10661,10 @@ version = "0.1.0" dependencies = [ "parity-scale-codec", "scale-info", + "sp-core", "sp-externalities", "sp-runtime-interface", + "subspace-runtime-primitives", "x509-parser 0.16.0", ] diff --git a/domains/pallets/auto-id/Cargo.toml b/domains/pallets/auto-id/Cargo.toml index 7bd3125425..a4a1f7d271 100644 --- a/domains/pallets/auto-id/Cargo.toml +++ b/domains/pallets/auto-id/Cargo.toml @@ -21,12 +21,12 @@ sp-auto-id = { version = "0.1.0", default-features = false, path = "../../primit sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } subspace-runtime-primitives = { version = "0.1.0", default-features = false, path = "../../../crates/subspace-runtime-primitives" } -x509-parser = { version = "0.16.0", default-features = false} [dev-dependencies] pem = "3.0.3" ring = "0.17.8" sp-io = { version = "23.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +x509-parser = { version = "0.16.0"} [features] default = ["std"] diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs index 32ec3dd433..8edbe18ab3 100644 --- a/domains/pallets/auto-id/src/lib.rs +++ b/domains/pallets/auto-id/src/lib.rs @@ -24,74 +24,26 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::collections::BTreeSet; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use codec::{Decode, Encode}; use frame_support::dispatch::DispatchResult; +use frame_support::ensure; use frame_support::traits::Time; -use frame_support::{ensure, PalletError}; pub use pallet::*; use scale_info::TypeInfo; -use sp_auto_id::signature_verification_runtime_interface::verify_signature; -use sp_auto_id::{DerVec, SignatureVerificationRequest}; +use sp_auto_id::signature_verification_runtime_interface::{ + decode_tbs_certificate, verify_signature, +}; +use sp_auto_id::{DerVec, SignatureVerificationRequest, Validity}; use sp_core::U256; #[cfg(feature = "std")] use std::collections::BTreeSet; use subspace_runtime_primitives::Moment; -use x509_parser::certificate::TbsCertificate; -use x509_parser::prelude::FromDer; /// Unique AutoId identifier. pub type Identifier = U256; -/// Validity of a given certificate. -#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] -pub struct Validity { - /// Not valid before the time since UNIX_EPOCH - pub not_before: Moment, - /// Not valid after the time since UNIX_EPOCH - pub not_after: Moment, -} - -impl Validity { - /// Checks if the certificate is valid at this time. - pub fn is_valid_at(&self, time: Moment) -> bool { - time >= self.not_before && time <= self.not_after - } -} - -/// Validity conversion error. -#[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)] -pub enum ValidityError { - Overflow, - Expired, -} - -impl From for Error { - fn from(value: ValidityError) -> Self { - Error::::InvalidValidity(value) - } -} - -impl TryFrom for Validity { - type Error = ValidityError; - - fn try_from(value: x509_parser::certificate::Validity) -> Result { - Ok(Validity { - not_before: (value.not_before.timestamp() as u64) - .checked_mul(1000) - .and_then(|secs| { - secs.checked_add(value.not_before.to_datetime().millisecond() as u64) - }) - .ok_or(Self::Error::Overflow)?, - not_after: (value.not_after.timestamp() as u64) - .checked_mul(1000) - .and_then(|secs| { - secs.checked_add(value.not_after.to_datetime().millisecond() as u64) - }) - .ok_or(Self::Error::Overflow)?, - }) - } -} - /// X509 certificate. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct X509Certificate { @@ -201,7 +153,7 @@ pub enum RegisterAutoId { #[frame_support::pallet] mod pallet { - use crate::{AutoId, Identifier, RegisterAutoId, Signature, ValidityError}; + use crate::{AutoId, Identifier, RegisterAutoId, Signature}; use frame_support::pallet_prelude::*; use frame_support::traits::Time; use frame_system::pallet_prelude::*; @@ -230,8 +182,6 @@ mod pallet { UnknownIssuer, /// Certificate is invalid, InvalidCertificate, - /// Invalid certificate validity. - InvalidValidity(ValidityError), /// Invalid signature. InvalidSignature, /// AutoId identifier overflow. @@ -307,33 +257,30 @@ impl Pallet { signature_algorithm, signature, } => { - let (_, tbs_certificate) = TbsCertificate::from_der(certificate.as_ref()) - .map_err(|_| Error::::InvalidCertificate)?; + let tbs_certificate = decode_tbs_certificate(certificate.clone()) + .ok_or(Error::::InvalidCertificate)?; let req = SignatureVerificationRequest { - public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), + public_key_info: tbs_certificate.subject_public_key_info.clone(), signature_algorithm, data: certificate.0.clone(), signature, }; verify_signature(req).ok_or(Error::::InvalidSignature)?; - let serial = U256::from_big_endian(&tbs_certificate.serial.to_bytes_be()); - let validity = Validity::try_from(tbs_certificate.validity) - .map_err(Error::::InvalidValidity)?; ensure!( - validity.is_valid_at(current_time), - Error::::InvalidValidity(ValidityError::Expired) + tbs_certificate.validity.is_valid_at(current_time), + Error::::ExpiredCertificate ); Certificate::X509(X509Certificate { issuer_id: None, - serial, - subject: tbs_certificate.subject.as_raw().to_vec().into(), - subject_public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), - validity, + serial: tbs_certificate.serial, + subject: tbs_certificate.subject, + subject_public_key_info: tbs_certificate.subject_public_key_info, + validity: tbs_certificate.validity, raw: certificate, - issued_serials: BTreeSet::from([serial]), + issued_serials: BTreeSet::from([tbs_certificate.serial]), revoked: false, }) } @@ -345,7 +292,8 @@ impl Pallet { } => { let mut issuer_auto_id = AutoIds::::get(issuer_id).ok_or(Error::::UnknownIssuer)?; - let issuer_pki = issuer_auto_id.certificate.subject_public_key_info(); + let issuer_public_key_info = + issuer_auto_id.certificate.subject_public_key_info(); ensure!( issuer_auto_id.certificate.is_valid_at(current_time), @@ -357,38 +305,35 @@ impl Pallet { Error::::CertificateRevoked ); - let (_, tbs_certificate) = TbsCertificate::from_der(certificate.as_ref()) - .map_err(|_| Error::::InvalidCertificate)?; + let tbs_certificate = decode_tbs_certificate(certificate.clone()) + .ok_or(Error::::InvalidCertificate)?; let req = SignatureVerificationRequest { - public_key_info: issuer_pki, + public_key_info: issuer_public_key_info, signature_algorithm, data: certificate.0.clone(), signature, }; verify_signature(req).ok_or(Error::::InvalidSignature)?; - let validity = Validity::try_from(tbs_certificate.validity) - .map_err(Error::::InvalidValidity)?; ensure!( - validity.is_valid_at(current_time), - Error::::InvalidValidity(ValidityError::Expired) + tbs_certificate.validity.is_valid_at(current_time), + Error::::ExpiredCertificate ); - let serial = U256::from_big_endian(&tbs_certificate.serial.to_bytes_be()); issuer_auto_id .certificate - .issue_certificate_serial::(serial)?; + .issue_certificate_serial::(tbs_certificate.serial)?; AutoIds::::insert(issuer_id, issuer_auto_id); Certificate::X509(X509Certificate { issuer_id: Some(issuer_id), - serial, - subject: tbs_certificate.subject.as_raw().to_vec().into(), - subject_public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), - validity, + serial: tbs_certificate.serial, + subject: tbs_certificate.subject, + subject_public_key_info: tbs_certificate.subject_public_key_info, + validity: tbs_certificate.validity, raw: certificate, - issued_serials: BTreeSet::from([serial]), + issued_serials: BTreeSet::from([tbs_certificate.serial]), revoked: false, }) } diff --git a/domains/primitives/auto-id/Cargo.toml b/domains/primitives/auto-id/Cargo.toml index e621b0ed08..e08381e021 100644 --- a/domains/primitives/auto-id/Cargo.toml +++ b/domains/primitives/auto-id/Cargo.toml @@ -16,8 +16,10 @@ include = [ [dependencies] codec = { package = "parity-scale-codec", version = "3.6.5", default-features = false, features = ["derive"] } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } +sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-externalities = { version = "0.19.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-runtime-interface = { version = "17.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +subspace-runtime-primitives = { version = "0.1.0", path = "../../../crates/subspace-runtime-primitives", default-features = false } x509-parser = { version = "0.16.0", features = ["verify"], optional = true } [features] @@ -25,8 +27,10 @@ default = ["std"] std = [ "codec/std", "scale-info/std", + "sp-core/std", "sp-externalities/std", "sp-runtime-interface/std", + "subspace-runtime-primitives/std", "x509-parser" ] diff --git a/domains/primitives/auto-id/src/host_functions.rs b/domains/primitives/auto-id/src/host_functions.rs index b0ab7758ef..1355653f82 100644 --- a/domains/primitives/auto-id/src/host_functions.rs +++ b/domains/primitives/auto-id/src/host_functions.rs @@ -1,4 +1,5 @@ -use crate::SignatureVerificationRequest; +use crate::{DerVec, SignatureVerificationRequest, TbsCertificate, Validity}; +use sp_core::U256; use std::sync::Arc; use x509_parser::der_parser::asn1_rs::BitString; use x509_parser::prelude::{AlgorithmIdentifier, FromDer, SubjectPublicKeyInfo}; @@ -7,6 +8,7 @@ use x509_parser::verify::verify_signature; /// Host function trait for Certificate registration pub trait HostFunctions: Send + Sync { fn verify_signature(&self, req: SignatureVerificationRequest) -> Option<()>; + fn decode_tbs_certificate(&self, certificate: DerVec) -> Option; } sp_externalities::decl_extension! { @@ -39,4 +41,17 @@ impl HostFunctions for HostFunctionsImpl { let signature = BitString::new(0, &signature); verify_signature(&public_key_info, &signature_algorithm, &signature, &data).ok() } + + fn decode_tbs_certificate(&self, certificate: DerVec) -> Option { + let (_, tbs_certificate) = + x509_parser::certificate::TbsCertificate::from_der(certificate.as_ref()).ok()?; + let serial = U256::from_big_endian(&tbs_certificate.serial.to_bytes_be()); + let validity = Validity::try_from(tbs_certificate.validity).ok()?; + Some(TbsCertificate { + serial, + subject: tbs_certificate.subject.as_raw().to_vec().into(), + subject_public_key_info: tbs_certificate.subject_pki.raw.to_vec().into(), + validity, + }) + } } diff --git a/domains/primitives/auto-id/src/lib.rs b/domains/primitives/auto-id/src/lib.rs index adfc1eb53d..bfcd160576 100644 --- a/domains/primitives/auto-id/src/lib.rs +++ b/domains/primitives/auto-id/src/lib.rs @@ -29,8 +29,10 @@ pub use crate::runtime_interface::signature_verification_runtime_interface; use alloc::vec::Vec; use codec::{Decode, Encode}; use scale_info::TypeInfo; +use sp_core::U256; use sp_runtime_interface::pass_by; use sp_runtime_interface::pass_by::PassBy; +use subspace_runtime_primitives::Moment; /// Signature verification request. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] @@ -49,10 +51,72 @@ impl PassBy for SignatureVerificationRequest { type PassBy = pass_by::Codec; } +/// Validity of a given certificate. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct Validity { + /// Not valid before the time since UNIX_EPOCH + pub not_before: Moment, + /// Not valid after the time since UNIX_EPOCH + pub not_after: Moment, +} + +impl Validity { + /// Checks if the certificate is valid at this time. + pub fn is_valid_at(&self, time: Moment) -> bool { + time >= self.not_before && time <= self.not_after + } +} + +/// Validity conversion error. +#[cfg(feature = "std")] +#[derive(TypeInfo, Encode, Decode, Debug, PartialEq)] +pub enum ValidityError { + Overflow, +} + +#[cfg(feature = "std")] +impl TryFrom for Validity { + type Error = ValidityError; + + fn try_from(value: x509_parser::certificate::Validity) -> Result { + Ok(Validity { + not_before: (value.not_before.timestamp() as u64) + .checked_mul(1000) + .and_then(|secs| { + secs.checked_add(value.not_before.to_datetime().millisecond() as u64) + }) + .ok_or(Self::Error::Overflow)?, + not_after: (value.not_after.timestamp() as u64) + .checked_mul(1000) + .and_then(|secs| { + secs.checked_add(value.not_after.to_datetime().millisecond() as u64) + }) + .ok_or(Self::Error::Overflow)?, + }) + } +} + +/// Decoded Tbs certificate. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct TbsCertificate { + /// Certificate serial number. + pub serial: U256, + /// Certificate subject. + pub subject: DerVec, + /// Certificate subject public key info. + pub subject_public_key_info: DerVec, + /// Certificate validity. + pub validity: Validity, +} + /// DER encoded bytes #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct DerVec(pub Vec); +impl PassBy for DerVec { + type PassBy = pass_by::Codec; +} + impl AsRef<[u8]> for DerVec { fn as_ref(&self) -> &[u8] { &self.0 diff --git a/domains/primitives/auto-id/src/runtime_interface.rs b/domains/primitives/auto-id/src/runtime_interface.rs index 25882cdd4d..4a8587ea7b 100644 --- a/domains/primitives/auto-id/src/runtime_interface.rs +++ b/domains/primitives/auto-id/src/runtime_interface.rs @@ -1,6 +1,6 @@ #[cfg(feature = "std")] use crate::host_functions::HostFunctionExtension; -use crate::SignatureVerificationRequest; +use crate::{DerVec, SignatureVerificationRequest, TbsCertificate}; #[cfg(feature = "std")] use sp_externalities::ExternalitiesExt; use sp_runtime_interface::runtime_interface; @@ -15,4 +15,12 @@ pub trait SignatureVerificationRuntimeInterface { ) .verify_signature(req) } + + fn decode_tbs_certificate(&mut self, certificate: DerVec) -> Option { + self.extension::() + .expect( + "No `CertificateRegistryHostFunctionExtension` associated for the current context!", + ) + .decode_tbs_certificate(certificate) + } } From 4863bb8b1160cf9bc197cd1d5fbfe0ee07899949 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 1 Apr 2024 14:04:51 +0530 Subject: [PATCH 8/9] rename RuntimeInterface for AutoId --- domains/pallets/auto-id/src/lib.rs | 4 +--- domains/primitives/auto-id/src/lib.rs | 2 +- domains/primitives/auto-id/src/runtime_interface.rs | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs index 8edbe18ab3..744702ea58 100644 --- a/domains/pallets/auto-id/src/lib.rs +++ b/domains/pallets/auto-id/src/lib.rs @@ -32,9 +32,7 @@ use frame_support::ensure; use frame_support::traits::Time; pub use pallet::*; use scale_info::TypeInfo; -use sp_auto_id::signature_verification_runtime_interface::{ - decode_tbs_certificate, verify_signature, -}; +use sp_auto_id::auto_id_runtime_interface::{decode_tbs_certificate, verify_signature}; use sp_auto_id::{DerVec, SignatureVerificationRequest, Validity}; use sp_core::U256; #[cfg(feature = "std")] diff --git a/domains/primitives/auto-id/src/lib.rs b/domains/primitives/auto-id/src/lib.rs index bfcd160576..8d44585108 100644 --- a/domains/primitives/auto-id/src/lib.rs +++ b/domains/primitives/auto-id/src/lib.rs @@ -24,7 +24,7 @@ mod runtime_interface; #[cfg(not(feature = "std"))] extern crate alloc; -pub use crate::runtime_interface::signature_verification_runtime_interface; +pub use crate::runtime_interface::auto_id_runtime_interface; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use codec::{Decode, Encode}; diff --git a/domains/primitives/auto-id/src/runtime_interface.rs b/domains/primitives/auto-id/src/runtime_interface.rs index 4a8587ea7b..d5aacbd961 100644 --- a/domains/primitives/auto-id/src/runtime_interface.rs +++ b/domains/primitives/auto-id/src/runtime_interface.rs @@ -5,9 +5,9 @@ use crate::{DerVec, SignatureVerificationRequest, TbsCertificate}; use sp_externalities::ExternalitiesExt; use sp_runtime_interface::runtime_interface; -/// Signature verification runtime interface. +/// AutoId runtime interface. #[runtime_interface] -pub trait SignatureVerificationRuntimeInterface { +pub trait AutoIdRuntimeInterface { fn verify_signature(&mut self, req: SignatureVerificationRequest) -> Option<()> { self.extension::() .expect( From 5de30543193d630a907a2822e6b4d28dd6f2cc57 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Tue, 2 Apr 2024 09:20:44 +0530 Subject: [PATCH 9/9] use certificate action as the signing data for signature verification for certificate actions --- domains/pallets/auto-id/src/lib.rs | 77 ++++++++++++++++++++++++---- domains/pallets/auto-id/src/tests.rs | 21 ++++++-- 2 files changed, 83 insertions(+), 15 deletions(-) diff --git a/domains/pallets/auto-id/src/lib.rs b/domains/pallets/auto-id/src/lib.rs index 744702ea58..6d5d678084 100644 --- a/domains/pallets/auto-id/src/lib.rs +++ b/domains/pallets/auto-id/src/lib.rs @@ -62,6 +62,8 @@ pub struct X509Certificate { pub issued_serials: BTreeSet, /// Signifies if the certificate is revoked. pub revoked: bool, + /// Certificate action nonce. + pub nonce: U256, } /// Certificate associated with AutoId. @@ -109,13 +111,29 @@ impl Certificate { Certificate::X509(cert) => cert.revoked, } } + + fn nonce(&self) -> U256 { + match self { + Certificate::X509(cert) => cert.nonce, + } + } + + fn inc_nonce(&mut self) -> DispatchResult { + match self { + Certificate::X509(cert) => { + cert.nonce = cert + .nonce + .checked_add(U256::one()) + .ok_or(Error::::NonceOverflow)?; + Ok(()) + } + } + } } /// A representation of AutoId #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct AutoId { - /// Unique AutoID identifier. - pub identifier: Identifier, /// Certificate associated with this AutoId. pub certificate: Certificate, } @@ -149,6 +167,21 @@ pub enum RegisterAutoId { X509(RegisterAutoIdX509), } +/// Specific action type taken by the subject of the Certificate. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum CertificateActionType { + RevokeCertificate, + DeactivateAutoId, +} + +/// Signing data used to verify the certificate action. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct CertificateAction { + pub id: Identifier, + pub nonce: U256, + pub action_type: CertificateActionType, +} + #[frame_support::pallet] mod pallet { use crate::{AutoId, Identifier, RegisterAutoId, Signature}; @@ -190,6 +223,8 @@ mod pallet { ExpiredCertificate, /// Certificate revoked. CertificateRevoked, + /// Nonce overflow. + NonceOverflow, } #[pallet::event] @@ -280,6 +315,7 @@ impl Pallet { raw: certificate, issued_serials: BTreeSet::from([tbs_certificate.serial]), revoked: false, + nonce: U256::zero(), }) } RegisterAutoIdX509::Leaf { @@ -333,6 +369,7 @@ impl Pallet { raw: certificate, issued_serials: BTreeSet::from([tbs_certificate.serial]), revoked: false, + nonce: U256::zero(), }) } }, @@ -344,10 +381,7 @@ impl Pallet { .ok_or(Error::::IdentifierOverflow)?; NextAutoIdIdentifier::::put(next_auto_id_identifier); - let auto_id = AutoId { - identifier: auto_id_identifier, - certificate, - }; + let auto_id = AutoId { certificate }; AutoIds::::insert(auto_id_identifier, auto_id); @@ -355,7 +389,11 @@ impl Pallet { Ok(()) } - fn do_verify_signature(auto_id: &AutoId, signature: Signature) -> DispatchResult { + fn do_verify_signature( + auto_id: &AutoId, + signing_data: CertificateAction, + signature: Signature, + ) -> DispatchResult { let Signature { signature_algorithm, value: signature, @@ -363,8 +401,7 @@ impl Pallet { let req = SignatureVerificationRequest { public_key_info: auto_id.certificate.subject_public_key_info(), signature_algorithm, - // uses auto_id identifier as the message to sign - data: auto_id.identifier.encode(), + data: signing_data.encode(), signature, }; @@ -377,13 +414,23 @@ impl Pallet { signature: Signature, ) -> DispatchResult { let mut auto_id = AutoIds::::get(auto_id_identifier).ok_or(Error::::UnknownIssuer)?; - Self::do_verify_signature(&auto_id, signature)?; + Self::do_verify_signature( + &auto_id, + CertificateAction { + id: auto_id_identifier, + nonce: auto_id.certificate.nonce(), + action_type: CertificateActionType::RevokeCertificate, + }, + signature, + )?; ensure!( !auto_id.certificate.is_revoked(), Error::::CertificateRevoked ); auto_id.certificate.revoke(); + auto_id.certificate.inc_nonce::()?; + // TODO: revoke all the issued leaf certificates if this is an issuer certificate. AutoIds::::insert(auto_id_identifier, auto_id); @@ -396,7 +443,15 @@ impl Pallet { signature: Signature, ) -> DispatchResult { let auto_id = AutoIds::::get(auto_id_identifier).ok_or(Error::::UnknownIssuer)?; - Self::do_verify_signature(&auto_id, signature)?; + Self::do_verify_signature( + &auto_id, + CertificateAction { + id: auto_id_identifier, + nonce: auto_id.certificate.nonce(), + action_type: CertificateActionType::DeactivateAutoId, + }, + signature, + )?; // TODO: remove all the AutoIds registered using leaf certificates if this is the issuer. AutoIds::::remove(auto_id_identifier); diff --git a/domains/pallets/auto-id/src/tests.rs b/domains/pallets/auto-id/src/tests.rs index 4088f1f1e7..dc10f7bed4 100644 --- a/domains/pallets/auto-id/src/tests.rs +++ b/domains/pallets/auto-id/src/tests.rs @@ -1,6 +1,7 @@ use crate::pallet::{AutoIds, NextAutoIdIdentifier}; use crate::{ - self as pallet_auto_id, Identifier, Pallet, RegisterAutoId, RegisterAutoIdX509, Signature, + self as pallet_auto_id, CertificateAction, CertificateActionType, Identifier, Pallet, + RegisterAutoId, RegisterAutoIdX509, Signature, }; use codec::Encode; use frame_support::dispatch::RawOrigin; @@ -9,7 +10,7 @@ use pem::parse; use ring::rand::SystemRandom; use ring::signature::RsaKeyPair; use sp_auto_id::DerVec; -use sp_core::H256; +use sp_core::{H256, U256}; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use sp_runtime::BuildStorage; use std::sync::Arc; @@ -198,7 +199,12 @@ fn test_revoke_certificate() { let auto_id_identifier = register_issuer_auto_id(); let auto_id = AutoIds::::get(auto_id_identifier).unwrap(); assert!(!auto_id.certificate.is_revoked()); - let signature = sign_preimage(auto_id_identifier.encode()); + let signing_data = CertificateAction { + id: auto_id_identifier, + nonce: auto_id.certificate.nonce(), + action_type: CertificateActionType::RevokeCertificate, + }; + let signature = sign_preimage(signing_data.encode()); Pallet::::revoke_certificate( RawOrigin::Signed(1).into(), auto_id_identifier, @@ -207,6 +213,7 @@ fn test_revoke_certificate() { .unwrap(); let auto_id = AutoIds::::get(auto_id_identifier).unwrap(); assert!(auto_id.certificate.is_revoked()); + assert_eq!(auto_id.certificate.nonce(), U256::one()); }) } @@ -214,7 +221,13 @@ fn test_revoke_certificate() { fn test_deactivate_auto_id() { new_test_ext().execute_with(|| { let auto_id_identifier = register_issuer_auto_id(); - let signature = sign_preimage(auto_id_identifier.encode()); + let auto_id = AutoIds::::get(auto_id_identifier).unwrap(); + let signing_data = CertificateAction { + id: auto_id_identifier, + nonce: auto_id.certificate.nonce(), + action_type: CertificateActionType::DeactivateAutoId, + }; + let signature = sign_preimage(signing_data.encode()); Pallet::::deactivate_auto_id( RawOrigin::Signed(1).into(), auto_id_identifier,