Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support web3name and linked accounts in the identity proof #525

Merged
merged 15 commits into from
May 25, 2023
Merged
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/dip-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;

#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub enum IdentityProofAction<Identifier, Proof, Details = ()> {
pub enum IdentityDetailsAction<Identifier, Proof, Details = ()> {
Updated(Identifier, Proof, Details),
Deleted(Identifier),
}
2 changes: 2 additions & 0 deletions crates/kilt-dip-support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ version.workspace = true
# Internal dependencies
did.workspace = true
pallet-dip-consumer.workspace = true
pallet-dip-provider.workspace = true

# Parity dependencies
parity-scale-codec = {workspace = true, features = ["derive"]}
Expand All @@ -32,6 +33,7 @@ default = ["std"]
std = [
"did/std",
"pallet-dip-consumer/std",
"pallet-dip-provider/std",
"parity-scale-codec/std",
"scale-info/std",
"frame-system/std",
Expand Down
159 changes: 124 additions & 35 deletions crates/kilt-dip-support/src/did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ use did::{
};
use frame_support::ensure;
use pallet_dip_consumer::{identity::IdentityDetails, traits::IdentityProofVerifier};
use pallet_dip_provider::traits::IdentityProvider;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_core::{ConstU64, Get, RuntimeDebug};
use sp_runtime::traits::CheckedSub;
use sp_std::marker::PhantomData;

use crate::{
merkle::ProofEntry,
merkle::RevealedDidKey,
traits::{Bump, DidDipOriginFilter},
};

Expand All @@ -40,8 +41,8 @@ pub struct TimeBoundDidSignature<BlockNumber> {
}

#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo)]
pub struct MerkleEntriesAndDidSignature<MerkleEntries, BlockNumber> {
pub merkle_entries: MerkleEntries,
pub struct MerkleLeavesAndDidSignature<MerkleLeaves, BlockNumber> {
pub merkle_leaves: MerkleLeaves,
pub did_signature: TimeBoundDidSignature<BlockNumber>,
}

Expand All @@ -53,6 +54,7 @@ pub struct MerkleEntriesAndDidSignature<MerkleEntries, BlockNumber> {
/// genesis_hash). Additional details can be added to the end of the tuple by
/// providing a `SignedExtraProvider`.
pub struct MerkleRevealedDidSignatureVerifier<
KeyId,
BlockNumber,
Digest,
Details,
Expand All @@ -67,6 +69,7 @@ pub struct MerkleRevealedDidSignatureVerifier<
>(
#[allow(clippy::type_complexity)]
PhantomData<(
KeyId,
BlockNumber,
Digest,
Details,
Expand All @@ -84,6 +87,7 @@ pub struct MerkleRevealedDidSignatureVerifier<
impl<
Call,
Subject,
KeyId,
BlockNumber,
Digest,
Details,
Expand All @@ -97,6 +101,7 @@ impl<
SignedExtra,
> IdentityProofVerifier<Call, Subject>
for MerkleRevealedDidSignatureVerifier<
KeyId,
BlockNumber,
Digest,
Details,
Expand All @@ -114,7 +119,7 @@ impl<
Call: Encode,
Digest: Encode,
Details: Bump + Encode,
MerkleProofEntries: AsRef<[ProofEntry<BlockNumber>]>,
MerkleProofEntries: AsRef<[RevealedDidKey<KeyId, BlockNumber>]>,
BlockNumberProvider: Get<BlockNumber>,
GenesisHashProvider: Get<Hash>,
Hash: Encode,
Expand All @@ -125,7 +130,7 @@ impl<
type Error = ();
/// The proof must be a list of Merkle leaves that have been previously
/// verified by the Merkle proof verifier, and the additional DID signature.
type Proof = MerkleEntriesAndDidSignature<MerkleProofEntries, BlockNumber>;
type Proof = MerkleLeavesAndDidSignature<MerkleProofEntries, BlockNumber>;
/// The `Details` that are part of the identity details must implement the
/// `Bump` trait.
type IdentityDetails = IdentityDetails<Digest, Details>;
Expand All @@ -135,11 +140,11 @@ impl<
/// the provided signature and its relationship to the DID subject.
type VerificationResult = (DidVerificationKey, DidVerificationKeyRelationship);

fn verify_proof_for_call_against_entry(
fn verify_proof_for_call_against_details(
call: &Call,
_subject: &Subject,
submitter: &Self::Submitter,
proof_entry: &mut Self::IdentityDetails,
identity_details: &mut Self::IdentityDetails,
proof: &Self::Proof,
) -> Result<Self::VerificationResult, Self::Error> {
let block_number = BlockNumberProvider::get();
Expand All @@ -151,43 +156,30 @@ impl<
// Signature generated at a future time, not possible to verify.
false
};

ensure!(is_signature_fresh, ());
let encoded_payload = (
call,
&proof_entry.details,
&identity_details.details,
submitter,
&proof.did_signature.block_number,
GenesisHashProvider::get(),
SignedExtraProvider::get(),
)
.encode();
// Only consider verification keys from the set of revealed Merkle leaves.
let mut proof_verification_keys = proof.merkle_entries.as_ref().iter().filter_map(
|ProofEntry {
key: DidPublicKeyDetails { key, .. },
relationship,
}| {
if let DidPublicKey::PublicVerificationKey(k) = key {
Some((
k,
DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold."),
))
} else {
None
}
},
);
// Only consider verification keys from the set of revealed keys.
let mut proof_verification_keys = proof.merkle_leaves.as_ref().iter().filter_map(|RevealedDidKey { relationship, details: DidPublicKeyDetails { key, .. }, .. } | {
let DidPublicKey::PublicVerificationKey(key) = key else { return None };
Some((key, DidVerificationKeyRelationship::try_from(*relationship).expect("Should never fail to build a VerificationRelationship from the given DidKeyRelationship because we have already made sure the conditions hold.")))
});
let valid_signing_key = proof_verification_keys.find(|(verification_key, _)| {
verification_key
.verify_signature(&encoded_payload, &proof.did_signature.signature)
.is_ok()
});
if let Some((key, relationship)) = valid_signing_key {
proof_entry.details.bump();
Ok((key.clone(), relationship))
} else {
Err(())
}
let Some((key, relationship)) = valid_signing_key else { return Err(()) };
identity_details.details.bump();
Ok((key.clone(), relationship))
}
}

Expand Down Expand Up @@ -223,17 +215,114 @@ where
/// `DidSignatureVerifier`.
type VerificationResult = DidSignatureVerifier::VerificationResult;

fn verify_proof_for_call_against_entry(
fn verify_proof_for_call_against_details(
call: &Call,
subject: &Subject,
submitter: &Self::Submitter,
proof_entry: &mut Self::IdentityDetails,
identity_details: &mut Self::IdentityDetails,
proof: &Self::Proof,
) -> Result<Self::VerificationResult, Self::Error> {
let did_signing_key =
DidSignatureVerifier::verify_proof_for_call_against_entry(call, subject, submitter, proof_entry, proof)
.map_err(|_| ())?;
let did_signing_key = DidSignatureVerifier::verify_proof_for_call_against_details(
call,
subject,
submitter,
identity_details,
proof,
)
.map_err(|_| ())?;
CallVerifier::check_call_origin_info(call, &did_signing_key).map_err(|_| ())?;
Ok(did_signing_key)
}
}

pub struct CombinedIdentityResult<OutputA, OutputB, OutputC> {
pub a: OutputA,
pub b: OutputB,
pub c: OutputC,
}

impl<OutputA, OutputB, OutputC> From<(OutputA, OutputB, OutputC)>
for CombinedIdentityResult<OutputA, OutputB, OutputC>
{
fn from(value: (OutputA, OutputB, OutputC)) -> Self {
Self {
a: value.0,
b: value.1,
c: value.2,
}
}
}

impl<OutputA, OutputB, OutputC> CombinedIdentityResult<OutputA, OutputB, OutputC>
where
OutputB: Default,
OutputC: Default,
{
pub fn from_a(a: OutputA) -> Self {
Self {
a,
b: OutputB::default(),
c: OutputC::default(),
}
}
}

impl<OutputA, OutputB, OutputC> CombinedIdentityResult<OutputA, OutputB, OutputC>
where
OutputA: Default,
OutputC: Default,
{
pub fn from_b(b: OutputB) -> Self {
Self {
a: OutputA::default(),
b,
c: OutputC::default(),
}
}
}

impl<OutputA, OutputB, OutputC> CombinedIdentityResult<OutputA, OutputB, OutputC>
where
OutputA: Default,
OutputB: Default,
{
pub fn from_c(c: OutputC) -> Self {
Self {
a: OutputA::default(),
b: OutputB::default(),
c,
}
}
}

pub struct CombineIdentityFrom<A, B, C>(PhantomData<(A, B, C)>);

impl<Identifier, A, B, C> IdentityProvider<Identifier> for CombineIdentityFrom<A, B, C>
where
A: IdentityProvider<Identifier>,
B: IdentityProvider<Identifier>,
C: IdentityProvider<Identifier>,
{
// TODO: Proper error handling
type Error = ();
type Success = CombinedIdentityResult<Option<A::Success>, Option<B::Success>, Option<C::Success>>;

fn retrieve(identifier: &Identifier) -> Result<Option<Self::Success>, Self::Error> {
match (
A::retrieve(identifier),
B::retrieve(identifier),
C::retrieve(identifier),
) {
// If no details is returned, return None for the whole result
(Ok(None), Ok(None), Ok(None)) => Ok(None),
// Otherwise, return `Some` or `None` depending on each result
(Ok(ok_a), Ok(ok_b), Ok(ok_c)) => Ok(Some(CombinedIdentityResult {
a: ok_a,
b: ok_b,
c: ok_c,
})),
// If any of them returns an `Err`, return an `Err`
_ => Err(()),
}
}
}
24 changes: 12 additions & 12 deletions crates/kilt-dip-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use pallet_dip_consumer::traits::IdentityProofVerifier;
use sp_std::marker::PhantomData;

use crate::did::MerkleEntriesAndDidSignature;
use crate::did::MerkleLeavesAndDidSignature;

pub mod did;
pub mod merkle;
Expand All @@ -50,42 +50,42 @@ where
DidSignatureVerifier: IdentityProofVerifier<
Call,
Subject,
Proof = MerkleEntriesAndDidSignature<MerkleProofVerifier::VerificationResult, BlockNumber>,
Proof = MerkleLeavesAndDidSignature<MerkleProofVerifier::VerificationResult, BlockNumber>,
IdentityDetails = MerkleProofVerifier::IdentityDetails,
Submitter = MerkleProofVerifier::Submitter,
>,
{
// FIXME: Better error handling
type Error = ();
// FIXME: Better type declaration
type Proof = MerkleEntriesAndDidSignature<MerkleProofVerifier::Proof, BlockNumber>;
type Proof = MerkleLeavesAndDidSignature<MerkleProofVerifier::Proof, BlockNumber>;
type IdentityDetails = DidSignatureVerifier::IdentityDetails;
type Submitter = MerkleProofVerifier::Submitter;
type VerificationResult = MerkleProofVerifier::VerificationResult;

fn verify_proof_for_call_against_entry(
fn verify_proof_for_call_against_details(
call: &Call,
subject: &Subject,
submitter: &Self::Submitter,
proof_entry: &mut Self::IdentityDetails,
identity_details: &mut Self::IdentityDetails,
proof: &Self::Proof,
) -> Result<Self::VerificationResult, Self::Error> {
let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_entry(
let merkle_proof_verification = MerkleProofVerifier::verify_proof_for_call_against_details(
call,
subject,
submitter,
proof_entry,
&proof.merkle_entries,
identity_details,
&proof.merkle_leaves,
)
.map_err(|_| ())?;
DidSignatureVerifier::verify_proof_for_call_against_entry(
DidSignatureVerifier::verify_proof_for_call_against_details(
call,
subject,
submitter,
proof_entry,
identity_details,
// FIXME: Remove `clone()` requirement
&MerkleEntriesAndDidSignature {
merkle_entries: merkle_proof_verification.clone(),
&MerkleLeavesAndDidSignature {
merkle_leaves: merkle_proof_verification.clone(),
did_signature: proof.did_signature.clone(),
},
)
Expand Down
Loading