Skip to content

Commit

Permalink
Merge a19914e into 29eaf57
Browse files Browse the repository at this point in the history
  • Loading branch information
abdulmth committed Nov 8, 2022
2 parents 29eaf57 + a19914e commit 569cf56
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 19 deletions.
3 changes: 2 additions & 1 deletion examples/0_basic/1_update_did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ async fn main() -> anyhow::Result<()> {
);

// Create a new DID in an Alias Output for us to modify.
let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?;
let (_, document, _): (Address, IotaDocument, KeyPair) = create_did(&client, &mut secret_manager).await?;
let did: IotaDID = document.id().clone();

// Resolve the latest state of the document.
let mut document: IotaDocument = client.resolve_did(&did).await?;
Expand Down
4 changes: 3 additions & 1 deletion examples/0_basic/2_resolve_did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use examples::create_did;
use examples::random_stronghold_path;
use examples::API_ENDPOINT;
use identity_iota::crypto::KeyPair;
use identity_iota::iota::block::address::Address;
use identity_iota::iota::IotaDID;
use identity_iota::iota::IotaDocument;
Expand All @@ -28,7 +29,8 @@ async fn main() -> anyhow::Result<()> {
);

// Create a new DID in an Alias Output for us to resolve.
let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?;
let (_, document, _): (Address, IotaDocument, KeyPair) = create_did(&client, &mut secret_manager).await?;
let did = document.id().clone();

// We can resolve a `IotaDID` with the client itself.
// Resolve the associated Alias Output and extract the DID document from it.
Expand Down
6 changes: 4 additions & 2 deletions examples/0_basic/3_deactivate_did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use examples::create_did;
use examples::random_stronghold_path;
use examples::API_ENDPOINT;
use identity_iota::crypto::KeyPair;
use identity_iota::iota::block::address::Address;
use identity_iota::iota::IotaClientExt;
use identity_iota::iota::IotaDID;
Expand All @@ -29,9 +30,10 @@ async fn main() -> anyhow::Result<()> {
);

// Create a new DID in an Alias Output for us to modify.
let (_, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?;
let (_, document, _): (Address, IotaDocument, KeyPair) = create_did(&client, &mut secret_manager).await?;
let did: IotaDID = document.id().clone();

// Resolve the latest state of the DID document, so we can reactivate it later.
// Resolve the latest state of the DID document.
let document: IotaDocument = client.resolve_did(&did).await?;

// Deactivate the DID by publishing an empty document.
Expand Down
5 changes: 4 additions & 1 deletion examples/0_basic/4_delete_did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
use examples::create_did;
use examples::random_stronghold_path;
use examples::API_ENDPOINT;
use identity_iota::crypto::KeyPair;
use identity_iota::iota::Error;
use identity_iota::iota::IotaClientExt;
use identity_iota::iota::IotaDID;
use identity_iota::iota::IotaDocument;
use identity_iota::iota::IotaIdentityClientExt;
use iota_client::block::address::Address;
use iota_client::secret::stronghold::StrongholdSecretManager;
Expand All @@ -27,7 +29,8 @@ async fn main() -> anyhow::Result<()> {
);

// Create a new DID in an Alias Output for us to modify.
let (address, did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?;
let (address, document, _): (Address, IotaDocument, KeyPair) = create_did(&client, &mut secret_manager).await?;
let did = document.id().clone();

// Deletes the Alias Output and its contained DID Document, rendering the DID permanently destroyed.
// This operation is *not* reversible.
Expand Down
91 changes: 91 additions & 0 deletions examples/0_basic/5_create_vc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use iota_client::block::address::Address;
use iota_client::secret::stronghold::StrongholdSecretManager;
use iota_client::secret::SecretManager;
use iota_client::Client;

use examples::create_did;
use examples::random_stronghold_path;
use examples::API_ENDPOINT;
use identity_iota::core::json;
use identity_iota::core::FromJson;
use identity_iota::core::ToJson;
use identity_iota::core::Url;
use identity_iota::credential::Credential;
use identity_iota::credential::CredentialBuilder;
use identity_iota::credential::CredentialValidationOptions;
use identity_iota::credential::CredentialValidator;
use identity_iota::credential::FailFast;
use identity_iota::credential::Subject;
use identity_iota::crypto::KeyPair;
use identity_iota::crypto::ProofOptions;
use identity_iota::did::DID;
use identity_iota::iota::IotaDocument;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Create a new client to interact with the IOTA ledger.
let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?;

// Create an identity for the issuer with one verification method `key-1`.
let mut secret_manager_issuer: SecretManager = SecretManager::Stronghold(
StrongholdSecretManager::builder()
.password("secure_password_1")
.build(random_stronghold_path())?,
);
let (_, issuer_document, key_pair): (Address, IotaDocument, KeyPair) =
create_did(&client, &mut secret_manager_issuer).await?;

// Create an identity for the holder, in this case also the subject.
let mut secret_manager_alice: SecretManager = SecretManager::Stronghold(
StrongholdSecretManager::builder()
.password("secure_password_2")
.build(random_stronghold_path())?,
);
let (_, alice_document, _): (Address, IotaDocument, KeyPair) = create_did(&client, &mut secret_manager_alice).await?;

// Create a credential subject indicating the degree earned by Alice.
let subject: Subject = Subject::from_json_value(json!({
"id": alice_document.id().as_str(),
"name": "Alice",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts",
},
"GPA": "4.0",
}))?;

// Build credential using subject above and issuer.
let mut credential: Credential = CredentialBuilder::default()
.id(Url::parse("https://example.edu/credentials/3732")?)
.issuer(Url::parse(issuer_document.id().as_str())?)
.type_("UniversityDegreeCredential")
.subject(subject)
.build()?;

// Sign the Credential with the issuer's verification method.
issuer_document.sign_data(&mut credential, key_pair.private(), "#key-1", ProofOptions::default())?;
println!("Credential JSON > {:#}", credential);

// Before sending this credential to the holder the issuer wants to validate that some properties
// of the credential satisfy their expectations.

// Validate the credential's signature using the issuer's DID Document, the credential's semantic structure,
// that the issuance date is not in the future and that the expiration date is not in the past:
CredentialValidator::validate(
&credential,
&issuer_document,
&CredentialValidationOptions::default(),
FailFast::FirstError,
)
.unwrap();

println!("VC successfully validated");

// The issuer is now sure that the credential they are about to issue satisfies their expectations.
// The credential is then serialized to JSON and transmitted to the subject in a secure manner.
let _credential_json: String = credential.to_json()?;
Ok(())
}
198 changes: 198 additions & 0 deletions examples/0_basic/6_create_vp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use iota_client::block::address::Address;
use iota_client::secret::stronghold::StrongholdSecretManager;
use iota_client::secret::SecretManager;
use iota_client::Client;

use examples::create_did;
use examples::random_stronghold_path;
use examples::API_ENDPOINT;
use identity_iota::core::json;
use identity_iota::core::Duration;
use identity_iota::core::FromJson;
use identity_iota::core::Timestamp;
use identity_iota::core::ToJson;
use identity_iota::core::Url;
use identity_iota::credential::Credential;
use identity_iota::credential::CredentialBuilder;
use identity_iota::credential::CredentialValidationOptions;
use identity_iota::credential::CredentialValidator;
use identity_iota::credential::FailFast;
use identity_iota::credential::Presentation;
use identity_iota::credential::PresentationBuilder;
use identity_iota::credential::PresentationValidationOptions;
use identity_iota::credential::PresentationValidator;
use identity_iota::credential::Subject;
use identity_iota::credential::SubjectHolderRelationship;
use identity_iota::crypto::KeyPair;
use identity_iota::crypto::ProofOptions;
use identity_iota::did::verifiable::VerifierOptions;
use identity_iota::did::DID;
use identity_iota::iota::IotaDocument;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
// ===========================================================================
// Step 1: Create identities for the issuer and the holder.
// ===========================================================================

// Create a new client to interact with the IOTA ledger.
let client: Client = Client::builder().with_primary_node(API_ENDPOINT, None)?.finish()?;

// Create an identity for the issuer with one verification method `key-1`.
let mut secret_manager_issuer: SecretManager = SecretManager::Stronghold(
StrongholdSecretManager::builder()
.password("secure_password_1")
.build(random_stronghold_path())?,
);
let (_, issuer_document, key_pair_issuer): (Address, IotaDocument, KeyPair) =
create_did(&client, &mut secret_manager_issuer).await?;

// Create an identity for the holder, in this case also the subject.
let mut secret_manager_alice: SecretManager = SecretManager::Stronghold(
StrongholdSecretManager::builder()
.password("secure_password_2")
.build(random_stronghold_path())?,
);
let (_, alice_document, key_pair_alice): (Address, IotaDocument, KeyPair) =
create_did(&client, &mut secret_manager_alice).await?;

// ===========================================================================
// Step 2: Issuer creates and signs a Verifiable Credential.
// ===========================================================================

// Create a credential subject indicating the degree earned by Alice.
let subject: Subject = Subject::from_json_value(json!({
"id": alice_document.id().as_str(),
"name": "Alice",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts",
},
"GPA": "4.0",
}))?;

// Build credential using subject above and issuer.
let mut credential: Credential = CredentialBuilder::default()
.id(Url::parse("https://example.edu/credentials/3732")?)
.issuer(Url::parse(issuer_document.id().as_str())?)
.type_("UniversityDegreeCredential")
.subject(subject)
.build()?;

// Sign the Credential with the issuer's verification method.
issuer_document.sign_data(
&mut credential,
key_pair_issuer.private(),
"#key-1",
ProofOptions::default(),
)?;
println!("Credential JSON > {:#}", credential);

// Before sending this credential to the holder the issuer wants to validate that some properties
// of the credential satisfy their expectations.

// Validate the credential's signature using the issuer's DID Document, the credential's semantic structure,
// that the issuance date is not in the future and that the expiration date is not in the past:
CredentialValidator::validate(
&credential,
&issuer_document,
&CredentialValidationOptions::default(),
FailFast::FirstError,
)
.unwrap();

println!("VC successfully validated");

// ===========================================================================
// Step 3: Issuer sends the Verifiable Credential to the holder.
// ===========================================================================

// The issuer is now sure that the credential they are about to issue satisfies their expectations.
// The credential is then serialized to JSON and transmitted to the subject in a secure manner.
let credential_json: String = credential.to_json()?;

// ===========================================================================
// Step 4: Verifier sends the holder a challenge and requests a signed Verifiable Presentation.
// ===========================================================================

// A unique random challenge generated by the requester per presentation can mitigate replay attacks
let challenge: &str = "475a7984-1bb5-4c4c-a56f-822bccd46440";

// The verifier and holder also agree that the signature should have an expiry date
// 10 minutes from now.
let expires: Timestamp = Timestamp::now_utc().checked_add(Duration::minutes(10)).unwrap();

// ===========================================================================
// Step 5: Holder creates and signs a verifiable presentation from the issued credential.
// ===========================================================================

// Deserialize the credential.
let credential: Credential = Credential::from_json(credential_json.as_str())?;

// Create an unsigned Presentation from the previously issued Verifiable Credential.
let mut presentation: Presentation = PresentationBuilder::default()
.holder(Url::parse(alice_document.id().as_ref())?)
.credential(credential)
.build()?;

// Sign the verifiable presentation using the holder's verification method
// and include the requested challenge and expiry timestamp.
alice_document.sign_data(
&mut presentation,
key_pair_alice.private(),
"#key-1",
ProofOptions::new().challenge(challenge.to_string()).expires(expires),
)?;

// ===========================================================================
// Step 6: Holder sends a verifiable presentation to the verifier.
// ===========================================================================

// Convert the Verifiable Presentation to JSON to send it to the verifier.
let presentation_json: String = presentation.to_json()?;

// ===========================================================================
// Step 7: Verifier receives the Verifiable Presentation and verifies it.
// ===========================================================================

// Deserialize the presentation from the holder:
let presentation: Presentation = Presentation::from_json(&presentation_json)?;

// The verifier wants the following requirements to be satisfied:
// - Signature verification (including checking the requested challenge to mitigate replay attacks)
// - Presentation validation must fail if credentials expiring within the next 10 hours are encountered
// - The presentation holder must always be the subject, regardless of the presence of the nonTransferable property
// - The issuance date must not be in the future.

let presentation_verifier_options: VerifierOptions = VerifierOptions::new()
.challenge(challenge.to_owned())
.allow_expired(false);

// Do not allow credentials that expire within the next 10 hours.
let credential_validation_options: CredentialValidationOptions = CredentialValidationOptions::default()
.earliest_expiry_date(Timestamp::now_utc().checked_add(Duration::hours(10)).unwrap());

let presentation_validation_options = PresentationValidationOptions::default()
.presentation_verifier_options(presentation_verifier_options.clone())
.shared_validation_options(credential_validation_options)
.subject_holder_relationship(SubjectHolderRelationship::AlwaysSubject);

PresentationValidator::validate(
&presentation,
&alice_document,
&[issuer_document],
&presentation_validation_options,
FailFast::FirstError,
)?;

// Since no errors were thrown by `verify_presentation` we know that the validation was successful.
println!("VP successfully validated");

// Note that we did not declare a latest allowed issuance date for credentials. This is because we only want to check
// that the credentials do not have an issuance date in the future which is a default check.

return Ok(());
}
3 changes: 2 additions & 1 deletion examples/1_advanced/0_did_controls_did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ async fn main() -> anyhow::Result<()> {
);

// Create a new DID for the company.
let (_, company_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?;
let (_, company_document, _): (Address, IotaDocument, KeyPair) = create_did(&client, &mut secret_manager).await?;
let company_did = company_document.id().clone();

// Get the current byte costs and network name.
let rent_structure: RentStructure = client.get_rent_structure()?;
Expand Down
5 changes: 4 additions & 1 deletion examples/1_advanced/1_did_issues_nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use examples::create_did;
use examples::random_stronghold_path;
use examples::API_ENDPOINT;
use identity_iota::crypto::KeyPair;
use identity_iota::iota::block::output::feature::MetadataFeature;
use identity_iota::iota::IotaDID;
use identity_iota::iota::IotaDocument;
Expand Down Expand Up @@ -52,7 +53,9 @@ async fn main() -> anyhow::Result<()> {
);

// Create a new DID for the manufacturer.
let (_, manufacturer_did): (Address, IotaDID) = create_did(&client, &mut secret_manager).await?;
let (_, manufacturer_document, _): (Address, IotaDocument, KeyPair) =
create_did(&client, &mut secret_manager).await?;
let manufacturer_did = manufacturer_document.id().clone();

// Get the current byte cost.
let rent_structure: RentStructure = client.get_rent_structure()?;
Expand Down
2 changes: 1 addition & 1 deletion examples/1_advanced/2_nft_owns_did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn main() -> anyhow::Result<()> {
let network: NetworkName = client.network_name().await?;

// Construct a DID document for the car.
let car_document: IotaDocument = create_did_document(&network)?;
let (car_document, _): (IotaDocument, _) = create_did_document(&network)?;

// Create a new DID for the car that is owned by the car NFT.
let car_did_output: AliasOutput = client
Expand Down

0 comments on commit 569cf56

Please sign in to comment.