Skip to content

Commit

Permalink
Store list of revoked serials, only issuer can revoke
Browse files Browse the repository at this point in the history
  • Loading branch information
dastansam committed Apr 26, 2024
1 parent 478d999 commit f2d0318
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 42 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-----
83 changes: 51 additions & 32 deletions domains/pallets/auto-id/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,22 @@ 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<T: Config>(&self) {
fn serial(&self) -> U256 {
match self {
Certificate::X509(cert) => {
if let Some(issuer_id) = cert.issuer_id {
CertificateRevocationList::<T>::insert(issuer_id, cert.serial);
} else {
// Root certificate
CertificateRevocationList::<T>::insert(self.derive_identifier(), cert.serial);
}
}
Certificate::X509(cert) => cert.serial,
}
}

/// 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.validity.is_valid_at(time),
}
}

Expand Down Expand Up @@ -208,8 +207,11 @@ 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,
}

Expand All @@ -219,6 +221,7 @@ mod pallet {
use frame_support::pallet_prelude::*;
use frame_support::traits::Time;
use frame_system::pallet_prelude::*;
use scale_info::prelude::collections::BTreeSet;

#[pallet::config]
pub trait Config: frame_system::Config {
Expand All @@ -236,16 +239,18 @@ mod pallet {

/// Stores list of revoked certificates.
///
/// It maps the unique identifier to the serial number of the certificate. Before accepting
/// 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, Serial, OptionQuery>;
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 Down Expand Up @@ -288,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 @@ -369,11 +376,6 @@ impl<T: Config> Pallet<T> {
Error::<T>::ExpiredCertificate
);

ensure!(
!CertificateRevocationList::<T>::contains_key(issuer_id),
Error::<T>::CertificateRevoked
);

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

Expand Down Expand Up @@ -447,27 +449,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 = match auto_id.certificate.issuer_id() {
Some(issuer_id) => issuer_id,
// self revoke
None => auto_id_identifier,
};

let mut issuer_auto_id = AutoIds::<T>::get(issuer_id).ok_or(Error::<T>::UnknownIssuer)?;

ensure!(
!CertificateRevocationList::<T>::get(issuer_id).map_or(false, |serials| serials
.iter()
.filter(|serial| *serial == &auto_id.certificate.serial()
|| *serial == &issuer_auto_id.certificate.serial())
.count()
> 0),
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!(
!CertificateRevocationList::<T>::contains_key(auto_id_identifier),
Error::<T>::CertificateAlreadyRevoked
);
CertificateRevocationList::<T>::mutate(issuer_id, |serials| {
serials
.get_or_insert_with(BTreeSet::new)
.insert(auto_id.certificate.serial());
});

auto_id.certificate.revoke::<T>();
auto_id.certificate.inc_nonce::<T>()?;

// 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
96 changes: 86 additions & 10 deletions domains/pallets/auto-id/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ impl Time for MockTime {
type Moment = Moment;

fn now() -> Self::Moment {
// valid block time for testing certs
1_711_367_658_200
// July 1, 2024, in milliseconds since Epoch
1_719_792_000_000
}
}

Expand Down Expand Up @@ -215,8 +215,12 @@ fn register_leaf_auto_id(issuer_auto_id: Identifier) -> Identifier {
auto_id_identifier
}

fn sign_preimage(data: Vec<u8>) -> Signature {
let priv_key_pem = include_str!("../res/private.issuer.pem");
fn sign_preimage(data: Vec<u8>, issuer: bool) -> Signature {
let priv_key_pem = if issuer {
include_str!("../res/private.issuer.pem")
} else {
include_str!("../res/private.leaf.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()];
Expand Down Expand Up @@ -278,7 +282,7 @@ fn test_register_issuer_auto_id_duplicate() {
}

#[test]
fn test_revoke_certificate() {
fn test_self_revoke_certificate() {
new_test_ext().execute_with(|| {
let auto_id_identifier = register_issuer_auto_id();
let auto_id = AutoIds::<Test>::get(auto_id_identifier).unwrap();
Expand All @@ -290,18 +294,90 @@ fn test_revoke_certificate() {
nonce: auto_id.certificate.nonce(),
action_type: CertificateActionType::RevokeCertificate,
};
let signature = sign_preimage(signing_data.encode());
let signature = sign_preimage(signing_data.encode(), true);
Pallet::<Test>::revoke_certificate(
RawOrigin::Signed(1).into(),
auto_id_identifier,
signature,
)
.unwrap();
let auto_id = AutoIds::<Test>::get(auto_id_identifier).unwrap();
assert!(CertificateRevocationList::<Test>::contains_key(
auto_id_identifier
));
assert!(CertificateRevocationList::<Test>::get(auto_id_identifier)
.unwrap()
.contains(&auto_id.certificate.serial()));

assert_eq!(auto_id.certificate.nonce(), U256::one());

// try revoking leaf certificate when issuer is revoked
let leaf_id = register_leaf_auto_id(auto_id_identifier);
let leaf_auto_id = AutoIds::<Test>::get(leaf_id).unwrap();
let signing_data = CertificateAction {
id: leaf_id,
nonce: leaf_auto_id.certificate.nonce(),
action_type: CertificateActionType::RevokeCertificate,
};
let signature = sign_preimage(signing_data.encode(), false);

assert_noop!(
Pallet::<Test>::revoke_certificate(RawOrigin::Signed(1).into(), leaf_id, signature),
Error::<Test>::CertificateAlreadyRevoked
);
})
}

#[test]
fn test_revoke_leaf_certificate() {
new_test_ext().execute_with(|| {
let issuer_id = register_issuer_auto_id();
let leaf_id = register_leaf_auto_id(issuer_id);
let issuer_auto_id = AutoIds::<Test>::get(issuer_id).unwrap();
let leaf_auto_id = AutoIds::<Test>::get(leaf_id).unwrap();

assert!(!CertificateRevocationList::<Test>::contains_key(issuer_id));

// leaf tries to revoke itself
let signing_data = CertificateAction {
id: leaf_id,
nonce: issuer_auto_id.certificate.nonce(),
action_type: CertificateActionType::RevokeCertificate,
};

// sign with leaf's private key
let signature = sign_preimage(signing_data.encode(), false);

// leaf tries to revoke itself
assert_noop!(
Pallet::<Test>::revoke_certificate(RawOrigin::Signed(1).into(), leaf_id, signature),
Error::<Test>::InvalidSignature
);

// now issuer revokes leaf
let signing_data = CertificateAction {
id: leaf_id,
nonce: issuer_auto_id.certificate.nonce(),
action_type: CertificateActionType::RevokeCertificate,
};
let signature = sign_preimage(signing_data.encode(), true);

Pallet::<Test>::revoke_certificate(RawOrigin::Signed(1).into(), leaf_id, signature)
.unwrap();

assert!(CertificateRevocationList::<Test>::get(issuer_id)
.unwrap()
.contains(&leaf_auto_id.certificate.serial()));

// revoking the same certificate again should fail
let signing_data = CertificateAction {
id: leaf_id,
nonce: issuer_auto_id.certificate.nonce(),
action_type: CertificateActionType::RevokeCertificate,
};
let signature = sign_preimage(signing_data.encode(), true);

assert_noop!(
Pallet::<Test>::revoke_certificate(RawOrigin::Signed(1).into(), leaf_id, signature),
Error::<Test>::CertificateAlreadyRevoked
);
})
}

Expand All @@ -315,7 +391,7 @@ fn test_deactivate_auto_id() {
nonce: auto_id.certificate.nonce(),
action_type: CertificateActionType::DeactivateAutoId,
};
let signature = sign_preimage(signing_data.encode());
let signature = sign_preimage(signing_data.encode(), true);
Pallet::<Test>::deactivate_auto_id(
RawOrigin::Signed(1).into(),
auto_id_identifier,
Expand Down

0 comments on commit f2d0318

Please sign in to comment.