Skip to content

Commit

Permalink
Merge pull request #2699 from subspace/auto-id-crl
Browse files Browse the repository at this point in the history
pallet-auto-id: add `CertificateRevocationList`
  • Loading branch information
dastansam authored May 1, 2024
2 parents 1d7ecd6 + 76c3a5f commit 60d733b
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 70 deletions.
Binary file modified domains/pallets/auto-id/res/issuer.cert.der
Binary file not shown.
Binary file modified domains/pallets/auto-id/res/leaf.cert.der
Binary file not shown.
28 changes: 28 additions & 0 deletions domains/pallets/auto-id/res/private.leaf.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCtMEHxsyDTMlk5
LsFtKWQPhPChw6KJUzwISIi3JPMf9KNyX6GvG7G33ZuHiQUP+BETaDmOfjfsEeCN
5z3vicUb9QBV8saAOhuUm0Fc3/ti+sfWaja2JF0dk2/Ov/J6DQyXq1LCX65Xm58c
9oZMeGuBYQQnxcUXkHPgpx3LBSbv7xi/1ozEp7hX+WrEyQYM1TcOGcym8DvXx4Be
IvUJh38W1wsFIimyVs6rjKzOn2zpYbW7I82cza8ppWjch+sVduLPhOSfyMKWfzmn
gCX+lvsYfoetAWeiwwp9SN1Qaup44ky5Hyupw7ZMola4J0SsdqrYR+KABVdUSFEZ
nIrYZMNxAgMBAAECgf9fvpxEihFNW494BloThnH8sKWQVsk2aTg/6ktx2B8fBGAe
/nhra5Xbl1nPDRSb1Mh/eULKYGCYYdf0m003pzKfc9omVJg+IMVJTVK//oV0j1qH
qAXeSLwxsvRAfCR7uKzhEBXwpnTfXaJLEo06qzunUizyz/h0QFJ6PJQKMA76MeC9
duVHz13TiuC5IYfHbvGuSlDVNAYFv2taglun2JY6CM57VOflIYioShEOCjMWje1q
t+7d8WFcJnuBQc3cd4mw3CR3g/2HSoUmb1Bn0pHQa0IvgschPCcFGXAwJQ6Cf/P/
U5BHTVkeeconjMYvDdyy+YvhBXo0UuRxxZbDBgECgYEA31kCvtvDFk0uCKZ2fB2H
qlwlA6V2Jki8edFsVHRD0DulKLLXnsy3t1UZA9TwfU20RYvDoVphbuaOHaONCtpz
ACkEZMtnOaorZSSnICbhnzuU7BPX/RNWa/34khbjy8YVCQ9h+Jo2l9GVAweZ8iBv
SO6M0hqGclSe/KnTnTkJAfECgYEAxoH+awk7FysqlKMN/2ugDQQitzJ7ZjJAcO/T
b+YKnjGmF3Onv/+IG2Nu++kpOaOoyZ+cb++kVGpT0rRmy+sjHpvvMJrD5GP0eiAl
nHckGCLf7gMx0rcyBmVVZOHzaHsHHRRoH9PzsSN91zKGhIU0XpdFjF1vLSUAblSO
br4OWYECgYBb+IBb7YzxIwkAwONripFyAo2vabQ0YaFTHHzabiH6noUNNE/78Vr5
oI4zeL0rLBM+zCXbzKbwjvoYlF+hB4FxoHJRuzyfj0ZdWPGFGN2xv0w8xpMbgJoG
0EdKiSh2ofPJjk8Omxo9/Cy7Wab4AIky5CCS6B9S9yuc6aXdST4/UQKBgA7MpkEo
oQUrLLOELIj8ZyRRSJ1L4DNQT8mbt7HB/syoeu+IqdsAnA8erKmPSomHkA/oHGuj
/CZm/vTYikltsGKZ0Y1YHH6sjQ+F0ggGQeSixPsjtdU13z7m0yUAS3tgoLkkSlcF
IEf2k2010R2UKMFcmczLMny1I4EWQMA03zEBAoGAWSzHaWtK5mbzM90B2CxFcFHH
tdCPxKspe0bnGcZkFSHYHVUn6Mgbe3szA2QN9uftBLmXP+FXvl7+woUExZaYZZzW
njyKn1VP9dxmme2NSx3PoIN1N8QCKD4xXqlI+U1nnhBaYuM7ieIzr0hID9rrZZ10
nCVF4arGqOL8Crkkqmg=
-----END PRIVATE KEY-----
146 changes: 92 additions & 54 deletions domains/pallets/auto-id/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ use subspace_runtime_primitives::Moment;
/// Unique AutoId identifier.
pub type Identifier = H256;

/// Serial issued by the subject.
pub type Serial = U256;

/// X509 certificate.
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct X509Certificate {
Expand All @@ -59,9 +62,7 @@ pub struct X509Certificate {
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<U256>,
/// Signifies if the certificate is revoked.
pub revoked: bool,
pub issued_serials: BTreeSet<Serial>,
/// Certificate action nonce.
pub nonce: U256,
}
Expand Down Expand Up @@ -101,22 +102,43 @@ impl Certificate {
}
}

/// Checks if the certificate is valid at this time.
pub(crate) fn is_valid_at(&self, time: Moment) -> bool {
fn issuer_id(&self) -> Option<Identifier> {
match self {
Certificate::X509(cert) => cert.validity.is_valid_at(time),
Certificate::X509(cert) => cert.issuer_id,
}
}

fn revoke(&mut self) {
fn serial(&self) -> Serial {
match self {
Certificate::X509(cert) => cert.revoked = true,
Certificate::X509(cert) => cert.serial,
}
}

fn is_revoked(&self) -> bool {
/// Checks if the certificate is valid at this time.
pub(crate) fn is_valid_at(&self, time: Moment) -> bool {
match self {
Certificate::X509(cert) => cert.revoked,
Certificate::X509(cert) => cert.validity.is_valid_at(time),
}
}

/// Deterministically derives an identifier from the certificate.
///
/// The identifier is derived by hashing the subject common name of the certificate.
/// If the certificate is a leaf certificate, the issuer identifier is combined with the subject common name.
fn derive_identifier(&self) -> Identifier {
match &self {
Certificate::X509(cert) => {
if let Some(issuer_id) = cert.issuer_id {
let mut data = issuer_id.to_fixed_bytes().to_vec();

data.extend_from_slice(cert.subject_common_name.as_ref());

blake2_256(&data).into()
} else {
// Root certificate
blake2_256(cert.subject_common_name.as_ref()).into()
}
}
}
}

Expand Down Expand Up @@ -146,29 +168,6 @@ pub struct AutoId {
pub certificate: Certificate,
}

impl AutoId {
/// Deterministically derives an identifier for the `AutoId`.
///
/// The identifier is derived by hashing the subject common name of the certificate.
/// If the certificate is a leaf certificate, the issuer identifier is combined with the subject common name.
fn derive_identifier(&self) -> Identifier {
match &self.certificate {
Certificate::X509(cert) => {
if let Some(issuer_id) = cert.issuer_id {
let mut data = issuer_id.to_fixed_bytes().to_vec();

data.extend_from_slice(cert.subject_common_name.as_ref());

blake2_256(&data).into()
} else {
// Root certificate
blake2_256(cert.subject_common_name.as_ref()).into()
}
}
}
}
}

/// Type holds X509 certificate details used to register an AutoId.
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub enum RegisterAutoIdX509 {
Expand Down Expand Up @@ -208,14 +207,18 @@ pub enum CertificateActionType {
/// Signing data used to verify the certificate action.
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct CertificateAction {
/// On which AutoId the action is taken.
pub id: Identifier,
/// Current nonce of the certificate.
pub nonce: U256,
/// Type of action taken.
pub action_type: CertificateActionType,
}

#[frame_support::pallet]
mod pallet {
use crate::{AutoId, Identifier, RegisterAutoId, Signature};
use super::*;
use crate::{AutoId, Identifier, RegisterAutoId, Serial, Signature};
use frame_support::pallet_prelude::*;
use frame_support::traits::Time;
use frame_system::pallet_prelude::*;
Expand All @@ -234,10 +237,20 @@ mod pallet {
#[pallet::storage]
pub(super) type AutoIds<T> = StorageMap<_, Identity, Identifier, AutoId, OptionQuery>;

/// Stores list of revoked certificates.
///
/// It maps the issuer's identifier to the list of revoked serial numbers of certificates. Before accepting
/// the certificate, external entities should check if the certificate or its issuer has been revoked.
#[pallet::storage]
pub(super) type CertificateRevocationList<T> =
StorageMap<_, Identity, Identifier, BTreeSet<Serial>, OptionQuery>;

#[pallet::error]
pub enum Error<T> {
/// Issuer auto id does not exist.
UnknownIssuer,
/// Unknown AutoId identifier.
UnknownAutoId,
/// Certificate is invalid,
InvalidCertificate,
/// Invalid signature.
Expand All @@ -248,6 +261,8 @@ mod pallet {
ExpiredCertificate,
/// Certificate revoked.
CertificateRevoked,
/// Certificate already revoked.
CertificateAlreadyRevoked,
/// Nonce overflow.
NonceOverflow,
/// Identifier already exists.
Expand Down Expand Up @@ -278,8 +293,10 @@ mod pallet {
}

/// Revokes a certificate associated with given AutoId.
#[pallet::call_index(1)]
///
/// The signature is verified against the issuer's public key.
// TODO: benchmark
#[pallet::call_index(1)]
#[pallet::weight({10_000})]
pub fn revoke_certificate(
origin: OriginFor<T>,
Expand Down Expand Up @@ -340,7 +357,6 @@ impl<T: Config> Pallet<T> {
validity: tbs_certificate.validity,
raw: certificate,
issued_serials: BTreeSet::from([tbs_certificate.serial]),
revoked: false,
nonce: U256::zero(),
})
}
Expand All @@ -360,11 +376,6 @@ impl<T: Config> Pallet<T> {
Error::<T>::ExpiredCertificate
);

ensure!(
!issuer_auto_id.certificate.is_revoked(),
Error::<T>::CertificateRevoked
);

let tbs_certificate = decode_tbs_certificate(certificate.clone())
.ok_or(Error::<T>::InvalidCertificate)?;

Expand All @@ -380,6 +391,16 @@ impl<T: Config> Pallet<T> {
Error::<T>::ExpiredCertificate
);

ensure!(
!CertificateRevocationList::<T>::get(issuer_id).map_or(false, |serials| {
serials.iter().any(|s| {
*s == issuer_auto_id.certificate.serial()
|| *s == tbs_certificate.serial
})
}),
Error::<T>::CertificateRevoked
);

issuer_auto_id
.certificate
.issue_certificate_serial::<T>(tbs_certificate.serial)?;
Expand All @@ -394,15 +415,14 @@ impl<T: Config> Pallet<T> {
validity: tbs_certificate.validity,
raw: certificate,
issued_serials: BTreeSet::from([tbs_certificate.serial]),
revoked: false,
nonce: U256::zero(),
})
}
},
};

let auto_id_identifier = certificate.derive_identifier();
let auto_id = AutoId { certificate };
let auto_id_identifier = auto_id.derive_identifier();

ensure!(
!AutoIds::<T>::contains_key(auto_id_identifier),
Expand Down Expand Up @@ -439,26 +459,44 @@ impl<T: Config> Pallet<T> {
auto_id_identifier: Identifier,
signature: Signature,
) -> DispatchResult {
let mut auto_id = AutoIds::<T>::get(auto_id_identifier).ok_or(Error::<T>::UnknownIssuer)?;
let auto_id = AutoIds::<T>::get(auto_id_identifier).ok_or(Error::<T>::UnknownAutoId)?;

let (issuer_id, mut issuer_auto_id) = match auto_id.certificate.issuer_id() {
Some(issuer_id) => (
issuer_id,
AutoIds::<T>::get(issuer_id).ok_or(Error::<T>::UnknownIssuer)?,
),
// self revoke
None => (auto_id_identifier, auto_id.clone()),
};

ensure!(
!CertificateRevocationList::<T>::get(issuer_id).map_or(false, |serials| {
serials.iter().any(|s| {
*s == auto_id.certificate.serial() || *s == issuer_auto_id.certificate.serial()
})
}),
Error::<T>::CertificateAlreadyRevoked
);

Self::do_verify_signature(
&auto_id,
&issuer_auto_id,
CertificateAction {
id: auto_id_identifier,
nonce: auto_id.certificate.nonce(),
nonce: issuer_auto_id.certificate.nonce(),
action_type: CertificateActionType::RevokeCertificate,
},
signature,
)?;
ensure!(
!auto_id.certificate.is_revoked(),
Error::<T>::CertificateRevoked
);

auto_id.certificate.revoke();
auto_id.certificate.inc_nonce::<T>()?;
CertificateRevocationList::<T>::mutate(issuer_id, |serials| {
serials
.get_or_insert_with(BTreeSet::new)
.insert(auto_id.certificate.serial());
});

// TODO: revoke all the issued leaf certificates if this is an issuer certificate.
AutoIds::<T>::insert(auto_id_identifier, auto_id);
issuer_auto_id.certificate.inc_nonce::<T>()?;
AutoIds::<T>::insert(issuer_id, issuer_auto_id);

Self::deposit_event(Event::<T>::CertificateRevoked(auto_id_identifier));
Ok(())
Expand Down
Loading

0 comments on commit 60d733b

Please sign in to comment.