Skip to content

Commit

Permalink
Credentials cannot be unrevoked with StatusList2021 (#1284)
Browse files Browse the repository at this point in the history
* credentials cannot be unrevoked with StatusList2021

* Add test for unsuspension of credentials

* fmt clippy

* unrevoking valid credential does nothing

* change error name, fix test

* Update identity_credential/src/revocation/status_list_2021/credential.rs

Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com>

* Update identity_credential/src/revocation/status_list_2021/credential.rs

Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com>

---------

Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com>
  • Loading branch information
UMR1352 and abdulmth authored Jan 26, 2024
1 parent 5e8953e commit a929510
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions identity_credential/src/revocation/status_list_2021/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const CREDENTIAL_SUBJECT_TYPE: &str = "StatusList2021";

/// [Error](std::error::Error) type that represents the possible errors that can be
/// encountered when dealing with [`StatusList2021Credential`]s.
#[derive(Clone, Debug, Error, strum::IntoStaticStr)]
#[derive(Clone, Debug, Error, strum::IntoStaticStr, PartialEq, Eq)]
pub enum StatusList2021CredentialError {
/// The provided [`Credential`] has more than one `credentialSubject`.
#[error("A StatusList2021Credential may only have one credentialSubject")]
Expand All @@ -34,9 +34,12 @@ pub enum StatusList2021CredentialError {
/// Inner status list failures.
#[error(transparent)]
StatusListError(#[from] StatusListError),
/// Missing status list id
/// Missing status list id.
#[error("Cannot set the status of a credential without a \"credentialSubject.id\".")]
Unreferenceable,
/// Credentials cannot be unrevoked.
#[error("A previously revoked credential cannot be unrevoked.")]
UnreversibleRevocation,
}

use crate::credential::Credential;
Expand Down Expand Up @@ -117,6 +120,11 @@ impl StatusList2021Credential {

/// Sets the credential status of a given [`Credential`],
/// mapping it to the `index`-th entry of this [`StatusList2021Credential`].
///
/// ## Note:
/// - A revoked credential cannot ever be unrevoked and will lead to a
/// [`StatusList2021CredentialError::UnreversibleRevocation`].
/// - Trying to set `revoked_or_suspended` to `false` for an already valid credential will have no impact.
pub fn set_credential_status(
&mut self,
credential: &mut Credential,
Expand All @@ -138,6 +146,10 @@ impl StatusList2021Credential {
/// Sets the `index`-th entry to `value`
pub(crate) fn set_entry(&mut self, index: usize, value: bool) -> Result<(), StatusList2021CredentialError> {
let mut status_list = self.status_list()?;
let entry_status = status_list.get(index)?;
if self.purpose() == StatusPurpose::Revocation && !value && entry_status {
return Err(StatusList2021CredentialError::UnreversibleRevocation);
}
status_list.set(index, value)?;
self.subject.encoded_list = status_list.into_encoded_str();

Expand Down Expand Up @@ -403,4 +415,35 @@ mod tests {
.expect("Failed to deserialize");
assert_eq!(credential.purpose(), StatusPurpose::Revocation);
}
#[test]
fn revoked_credential_cannot_be_unrevoked() {
let url = Url::parse("http://example.com").unwrap();
let mut status_list_credential = StatusList2021CredentialBuilder::new(StatusList2021::default())
.issuer(Issuer::Url(url.clone()))
.purpose(StatusPurpose::Revocation)
.subject_id(url)
.build()
.unwrap();

assert!(status_list_credential.set_entry(420, false).is_ok());
status_list_credential.set_entry(420, true).unwrap();
assert_eq!(
status_list_credential.set_entry(420, false),
Err(StatusList2021CredentialError::UnreversibleRevocation)
);
}
#[test]
fn suspended_credential_can_be_unsuspended() {
let url = Url::parse("http://example.com").unwrap();
let mut status_list_credential = StatusList2021CredentialBuilder::new(StatusList2021::default())
.issuer(Issuer::Url(url.clone()))
.purpose(StatusPurpose::Suspension)
.subject_id(url)
.build()
.unwrap();

assert!(status_list_credential.set_entry(420, false).is_ok());
status_list_credential.set_entry(420, true).unwrap();
assert!(status_list_credential.set_entry(420, false).is_ok());
}
}

0 comments on commit a929510

Please sign in to comment.