Skip to content

Commit

Permalink
feat: support web3name and linked accounts in the identity proof (#525)
Browse files Browse the repository at this point in the history
  • Loading branch information
ntn-x2 authored May 25, 2023
1 parent 17c587e commit 4064da6
Show file tree
Hide file tree
Showing 29 changed files with 816 additions and 328 deletions.
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

0 comments on commit 4064da6

Please sign in to comment.