Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion concordium-base
Submodule concordium-base updated 41 files
+1 −2 .gitignore
+2 −0 rust-src/concordium_base/CHANGELOG.md
+8 −0 rust-src/concordium_base/README.md
+1 −0 rust-src/concordium_base/src/bulletproofs/inner_product_proof.rs
+2 −2 rust-src/concordium_base/src/bulletproofs/range_proof.rs
+1 −0 rust-src/concordium_base/src/bulletproofs/set_membership_proof.rs
+1 −0 rust-src/concordium_base/src/bulletproofs/set_non_membership_proof.rs
+1 −0 rust-src/concordium_base/src/eddsa_ed25519/dlog_ed25519.rs
+6 −4 rust-src/concordium_base/src/id/account_holder.rs
+1 −0 rust-src/concordium_base/src/id/chain.rs
+1 −0 rust-src/concordium_base/src/id/id_prover.rs
+1 −0 rust-src/concordium_base/src/id/id_verifier.rs
+1,243 −0 rust-src/concordium_base/src/id/identity_attributes_credentials.rs
+1 −0 rust-src/concordium_base/src/id/identity_attributes_credentials_stable_proof.hex
+2 −0 rust-src/concordium_base/src/id/identity_provider.rs
+1 −0 rust-src/concordium_base/src/id/mod.rs
+2 −1 rust-src/concordium_base/src/id/test.rs
+182 −2 rust-src/concordium_base/src/id/types.rs
+309 −23 rust-src/concordium_base/src/random_oracle/mod.rs
+2 −0 rust-src/concordium_base/src/sigma_protocols/aggregate_dlog.rs
+1 −0 rust-src/concordium_base/src/sigma_protocols/com_enc_eq.rs
+1 −0 rust-src/concordium_base/src/sigma_protocols/com_eq.rs
+1 −0 rust-src/concordium_base/src/sigma_protocols/com_eq_different_groups.rs
+2 −0 rust-src/concordium_base/src/sigma_protocols/com_eq_sig.rs
+1 −0 rust-src/concordium_base/src/sigma_protocols/com_ineq.rs
+3 −0 rust-src/concordium_base/src/sigma_protocols/com_lin.rs
+2 −0 rust-src/concordium_base/src/sigma_protocols/com_mult.rs
+1 −0 rust-src/concordium_base/src/sigma_protocols/dlog.rs
+2 −0 rust-src/concordium_base/src/sigma_protocols/enc_trans.rs
+1 −0 rust-src/concordium_base/src/sigma_protocols/mod.rs
+933 −0 rust-src/concordium_base/src/sigma_protocols/ps_sig_known.rs
+2 −0 rust-src/concordium_base/src/sigma_protocols/vcom_eq.rs
+237 −818 rust-src/concordium_base/src/web3id/mod.rs
+2,128 −0 rust-src/concordium_base/src/web3id/proofs.rs
+188 −0 rust-src/concordium_base/src/web3id/test.rs
+23 −0 rust-src/docs/assets/katex-header.html
+35 −1 rust-src/wallet_library/resources/web3_id_request.json
+60 −0 rust-src/wallet_library/resources/web3_id_request_v1challenge.json
+30 −7 rust-src/wallet_library/src/proofs.rs
+9 −0 rust-src/wallet_library/src/statement.rs
+7 −0 rust-src/wallet_library/src/test_helpers.rs
263 changes: 242 additions & 21 deletions src/web3id.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
//! Functionality for retrieving, verifying, and registering web3id credentials.

use std::collections::BTreeMap;

use crate::{
cis4::{Cis4Contract, Cis4QueryError},
types::queries::BlockInfo,
v2::{self, BlockIdentifier, IntoBlockIdentifier},
};
pub use concordium_base::web3id::*;
use concordium_base::{
base::CredentialRegistrationID,
cis4_types::CredentialStatus,
contracts_common::AccountAddress,
id::{constants::ArCurve, types::IpIdentity},
id::{
constants::{ArCurve, IpPairing},
types::{ArInfo, ArInfos, CredentialValidity, IpIdentity, IpInfo},
},
web3id,
};
use futures::TryStreamExt;
use futures::{TryFutureExt, TryStreamExt};

#[derive(thiserror::Error, Debug)]
pub enum CredentialLookupError {
Expand Down Expand Up @@ -46,7 +52,7 @@ pub struct CredentialWithMetadata {
/// The status of the credential at a point in time.
pub status: CredentialStatus,
/// The extra public inputs needed for verification.
pub inputs: CredentialsInputs<ArCurve>,
pub inputs: CredentialsInputs<IpPairing, ArCurve>,
}

/// Retrieve and validate credential metadata in a particular block.
Expand Down Expand Up @@ -110,24 +116,17 @@ pub async fn verify_credential_metadata(
cdv,
commitments,
} => {
let now = client.get_block_info(bi).await?.response.block_slot_time;
let valid_from = cdv.policy.created_at.lower().ok_or_else(|| {
CredentialLookupError::InvalidResponse(
"Credential creation date is not valid.".into(),
)
})?;
let valid_until = cdv.policy.valid_to.upper().ok_or_else(|| {
CredentialLookupError::InvalidResponse(
"Credential creation date is not valid.".into(),
)
})?;
let status = if valid_from > now {
CredentialStatus::NotActivated
} else if valid_until < now {
CredentialStatus::Expired
} else {
CredentialStatus::Active
let block_info = client.get_block_info(bi).await?.response;

// create the credential validity reference
let validity = CredentialValidity {
created_at: cdv.policy.created_at,
valid_to: cdv.policy.valid_to,
};

// determine credential validity status
let status = determine_credential_validity_status(validity, block_info)?;

let inputs = CredentialsInputs::Account {
commitments: commitments.cmm_attributes.clone(),
};
Expand All @@ -146,6 +145,112 @@ pub async fn verify_credential_metadata(

Ok(CredentialWithMetadata { status, inputs })
}

CredentialMetadata::Identity { issuer, validity } => {
// get all the identity providers at current block
let identity_providers = client
.get_identity_providers(bi)
.await?
.response
.try_collect::<Vec<_>>()
.map_err(|_e| {
CredentialLookupError::InvalidResponse(
"Error getting identity providers".into(),
)
})
.await?;

// get anonymity revokers
let anonymity_revokers = client
.get_anonymity_revokers(bi)
.await?
.response
.try_collect::<Vec<_>>()
.map_err(|_e| {
CredentialLookupError::InvalidResponse(
"Error while getting annonymity revokers.".into(),
)
})
.await?;

let block_info = client.get_block_info(bi).await?.response;

// call verify now for the data gathered
verify_identity_credential_metadata(
block_info,
issuer,
identity_providers,
anonymity_revokers,
validity,
)
}
}
}

/// verify metadata for an identity
fn verify_identity_credential_metadata(
block_info: BlockInfo,
issuer: IpIdentity,
identity_providers: Vec<IpInfo<IpPairing>>,
anonymity_revokers: Vec<ArInfo<ArCurve>>,
validity: CredentialValidity,
) -> Result<CredentialWithMetadata, CredentialLookupError> {
// get the matching identity provider
let matching_idp = identity_providers
.iter()
.find(|idp| idp.ip_identity.0 == issuer.0)
.ok_or(CredentialLookupError::InvalidResponse(
"Error occurred while getting matching identity provider".into(),
))?;

// create a new BTreeMap to hold the Anonymity revoker identity -> the anonymity revoker info
let mut anonymity_revoker_infos = BTreeMap::new();

for ar in anonymity_revokers {
anonymity_revoker_infos.insert(ar.ar_identity, ar);
}

// build inputs
let inputs = CredentialsInputs::Identity {
ip_info: matching_idp.clone(),
ars_infos: ArInfos {
anonymity_revokers: anonymity_revoker_infos,
},
};

// determine the credential validity status
let status = determine_credential_validity_status(validity, block_info)?;

Ok(CredentialWithMetadata { inputs, status })
}

/// Checks the credentials validity for the block info provided and returns a Credential Status
fn determine_credential_validity_status(
validity: CredentialValidity,
block_info: BlockInfo,
) -> Result<CredentialStatus, CredentialLookupError> {
let valid_from = validity
.created_at
.lower()
.ok_or(CredentialLookupError::InvalidResponse(
"Credential valid from date is not valid.".into(),
))?;

let valid_to = validity
.valid_to
.upper()
.ok_or(CredentialLookupError::InvalidResponse(
"Credential valid to date is not valid.".into(),
))?;

let now = block_info.block_slot_time;

if valid_from > now {
Ok(CredentialStatus::NotActivated)
} else if valid_to >= now {
Ok(CredentialStatus::Active)
} else {
Ok(CredentialStatus::Expired)
}
}

Expand All @@ -160,7 +265,7 @@ pub async fn verify_credential_metadata(
pub async fn get_public_data(
client: &mut v2::Client,
network: web3id::did::Network,
presentation: &web3id::Presentation<ArCurve, web3id::Web3IdAttribute>,
presentation: &web3id::Presentation<IpPairing, ArCurve, web3id::Web3IdAttribute>,
bi: impl IntoBlockIdentifier,
) -> Result<Vec<CredentialWithMetadata>, CredentialLookupError> {
let block = bi.into_block_identifier();
Expand All @@ -173,3 +278,119 @@ pub async fn get_public_data(
.collect::<futures::stream::FuturesOrdered<_>>();
stream.try_collect().await
}

#[cfg(test)]
mod tests {
use crate::types::queries::ProtocolVersionInt;

use super::*;
use chrono::{DateTime, Utc};
use concordium_base::{
base::{AbsoluteBlockHeight, BlockHeight, Energy, GenesisIndex, ProtocolVersion},
constants::SHA256,
hashes::HashBytes,
id::types::YearMonth,
};

#[test]
fn test_determine_credential_validity_status_as_active() {
let now = YearMonth::now();
let now_time = now.lower().expect("expected now time");
// create an 'active' credential validity, created last year, expires next year
let validity = CredentialValidity {
created_at: YearMonth {
month: now.month,
year: now.year - 1,
},
valid_to: YearMonth {
month: now.month,
year: now.year + 1,
},
};

// stub the current block information
let block_info = get_dummy_block_info(now_time);

let credential_status_result = determine_credential_validity_status(validity, block_info)
.expect("expected credential status here");

assert_eq!(CredentialStatus::Active, credential_status_result);
}

#[test]
fn test_determine_credential_validity_status_as_expired() {
let now = YearMonth::now();
let now_time = now.lower().expect("expected now time");

// create an 'expired' credential validity, created last year, expires 2 month ago
let validity = CredentialValidity {
created_at: YearMonth {
month: now.month,
year: now.year - 1,
},
valid_to: YearMonth {
month: now.month - 2,
year: now.year,
},
};

// stub the current block information
let block_info = get_dummy_block_info(now_time);

let credential_status_result = determine_credential_validity_status(validity, block_info)
.expect("expected credential status here");

assert_eq!(CredentialStatus::Expired, credential_status_result);
}

#[test]
fn test_determine_credential_validity_status_as_not_active() {
let now = YearMonth::now();
let now_time = now.lower().expect("expected now time");

// create a 'not active' credential validity, created 1 month in future, expires 1 year in future
let validity = CredentialValidity {
created_at: YearMonth {
month: now.month + 1,
year: now.year,
},
valid_to: YearMonth {
month: now.month,
year: now.year + 1,
},
};

// stub the current block information
let block_info = get_dummy_block_info(now_time);

let credential_status_result = determine_credential_validity_status(validity, block_info)
.expect("expected credential status here");

assert_eq!(CredentialStatus::NotActivated, credential_status_result);
}

// helper util to just get a dummy block based on a block slot time provided for credential validity testing
fn get_dummy_block_info(block_slot_time: DateTime<Utc>) -> BlockInfo {
BlockInfo {
transactions_size: 0u64,
block_parent: HashBytes::new([1u8; SHA256]),
block_hash: HashBytes::new([1u8; SHA256]),
finalized: true,
block_state_hash: HashBytes::new([1u8; SHA256]),
block_arrive_time: block_slot_time,
block_receive_time: block_slot_time,
transaction_count: 0,
transaction_energy_cost: Energy::default(),
block_slot: None,
block_last_finalized: HashBytes::new([1u8; SHA256]),
block_slot_time: block_slot_time,
block_height: AbsoluteBlockHeight { height: 1u64 },
era_block_height: BlockHeight { height: 1u64 },
genesis_index: GenesisIndex { height: 0u32 },
block_baker: None,
protocol_version: ProtocolVersionInt::from_enum(ProtocolVersion::P9),
round: None,
epoch: None,
}
}
}