Skip to content

Commit

Permalink
feat: add DID signature verification and replay protection (#516)
Browse files Browse the repository at this point in the history
Fixes KILTprotocol/ticket#2553.

The main changes are:
* Removed half-baked versioning support, to re-introduce a proper one
once versioning and version negotiation will be tackeld
* Moved most of the logic from the `runtime-common` crate to a new
`kilt-dip-support` crate, after making all the types generic over all
the runtime configurations
* Updated the `pallet-dip-consumer` pallet to have a bunch more details,
more info below
* Updated the XCM-simulator based tests

## Updates to the `dip-support` crate

The `dip-support` does not contain any versioned types anymore. Support
for versioning was half-baked and has been removed. Proper thought
around versioning and version negotiation will happen in the (short)
future.

## New `kilt-dip-support` crate

The new `kilt-dip-support` crate contains types and traits that are not
generic over ANY DIP deployment, but that can be used by every consumer
that relies on the KILT chain as their provider. Hence, types are
opinionated towards the way KILT implements the DIP protocol, but
generic over the runtime configuration.
The most relevant types are `MerkleProofAndDidSignatureVerifier`,
exported from the root, `DidMerkleProofVerifier`, exported from the
`merkle` module, and `DidSignatureAndCallVerifier`, exported from the
`did` module. When they are chained together, they allow a user to
submit a Merkle proof revealing parts of their DID Document, and to use
one of the revealed keys to sign a specifically-crafted payload which
provides authenticity guarantees of the DID subject.

The default verification logic for the DID signature is the following:
1. Verify that the block number specified in the signature is no older
than `SIGNATURE_VALIDITY` blocks, as specified by the runtime
2. Verify that the signature can be verified over the encoded tuple
formed by `(call, identity_details, submitter_address, block_number,
genesis_hash, signed_extra)`, where `signed_extra` can be anything more
that the runtime might require be included in the signature.
3. Verify that one of the keys revealed in the Merkle proof can be used
to verify the provided signature
4. [OPTIONAL] If the type variant which also performs authorization
checks on the call itself is used, i.e., the
`DidSignatureAndCallVerifier` type, then the verification will also
include checking whether the used verification relationship can be used
to dispatch the specified call or not, as we do ourselves in our did
pallet within the `submit_did_call`, but in a more generic way.

The verification logic for Merkle proofs has remained unchanged, and it
can now be chained with the DID verification logic.

## Refreshed `pallet-dip-consumer`

The pallet has been updated so that now the `dispatch_as` extrinsic
takes a generic `Proof` instead of an `IdentityProof`, and internally
performs the following checks:

1. Verify the origin is a signed origin (as before)
2. Verify that the call passes a preliminary filter, before any heavy
computation is executed on the provided proof, via the
`DipCallOriginFilter` type. This step will typically immediately filter
out any calls that cannot be called with a Dip origin regardless of the
content of the proof.
3. Retrieves the identity details from storage, if they exist
4. Delegate everything to the `ProofVerifier`, which has a mutable
reference to the details fetched at step 3, in case they need to update
some values that will be written to storage (e.g., the nonce)
5. The maybe mutated details are written back into storage
6. The call is dispatched with the new `DipOrigin`, which carries the
additional information as returned by the `ProofVerifier`. In the demo
runtime, this will be the set of keys revealed as part of the Merkle
proof, that have been verified, parsed, and used for verifying the DID
signature provided as part of the proof.

The pallet logic is very simple, and most of the (complex) logic lies in
the types that have been exposed as part of the `kilt-dip-support`
crate. This makes the pallet easier to understand, and more generic to
be used in different contexts potentially for different identity
providers as well, where each provider might require users to provide a
different identity proof.

## Minor changes

Other things have been updated to reflect the relocation of some files
into the new `kilt-dip-support` crate.

## How to test

1. Build [this version](KILTprotocol/sdk-js#751)
of the SDK
2. Clone and `yarn install && yarn build` [this
version](https://github.com/KILTprotocol/polkadot-apps/pull/8) of the
PolkadotJS apps
3. Copy-paste the `@kiltprotocol/type-definitions` output into the root
`node_modules` folder of the PolkadotJS apps
4. Follow the steps to set up the HRMP channels, create the DIDs, and
push the identity commitment from provider to consumer
5. Use [this
version](KILTprotocol/kilt-did-utilities#14) of
the kilt-did-utilities CLI tool to generate a valid DIP DID signature.
6. Enjoy ☀️☀️☀️
  • Loading branch information
ntn-x2 authored May 22, 2023
1 parent 779acdc commit 36b2f45
Show file tree
Hide file tree
Showing 31 changed files with 1,075 additions and 757 deletions.
23 changes: 20 additions & 3 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ parachain-staking = {path = "pallets/parachain-staking", default-features = fals
public-credentials = {path = "pallets/public-credentials", default-features = false}

# Internal support (with default disabled)
dip-support = {path = "crates/dip", default-features = false}
dip-support = {path = "crates/dip-support", default-features = false}
kilt-asset-dids = {path = "crates/assets", default-features = false}
kilt-dip-support = {path = "crates/kilt-dip-support", default-features = false}
kilt-support = {path = "support", default-features = false}
runtime-common = {path = "runtimes/common", default-features = false}

Expand Down
2 changes: 0 additions & 2 deletions crates/dip/Cargo.toml → crates/dip-support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ version.workspace = true
# Parity dependencies
parity-scale-codec = {workspace = true, features = ["derive"]}
scale-info = {workspace = true, features = ["derive"]}
sp-std.workspace = true

# Substrate dependencies
frame-support.workspace = true
Expand All @@ -24,6 +23,5 @@ default = ["std"]
std = [
"parity-scale-codec/std",
"scale-info/std",
"sp-std/std",
"frame-support/std"
]
14 changes: 5 additions & 9 deletions crates/dip/src/v1.rs → crates/dip-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,16 @@

// If you feel like getting in touch with us, you can do so at [email protected]

// TODO: Crate documentation

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::RuntimeDebug;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_std::vec::Vec;

#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)]
pub enum IdentityProofAction<Identifier, Proof, Details = ()> {
Updated(Identifier, Proof, Details),
Deleted(Identifier),
}

#[derive(Encode, Decode, RuntimeDebug, Clone, Eq, PartialEq, TypeInfo, Default)]
pub struct Proof<BlindedValue, Leaf> {
pub blinded: BlindedValue,
// TODO: Probably replace with a different data structure for better lookup capabilities
pub revealed: Vec<Leaf>,
}
73 changes: 0 additions & 73 deletions crates/dip/src/lib.rs

This file was deleted.

44 changes: 44 additions & 0 deletions crates/kilt-dip-support/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[package]
authors.workspace = true
description = "Support types, traits, and functions for the KILT Decentralized Identity Provider (DIP) functionality as implemented by the KILT blockchain."
documentation.workspace = true
edition.workspace = true
homepage.workspace = true
license-file.workspace = true
name = "kilt-dip-support"
readme.workspace = true
repository.workspace = true
version.workspace = true

[dependencies]
# Internal dependencies
did.workspace = true
pallet-dip-consumer.workspace = true

# Parity dependencies
parity-scale-codec = {workspace = true, features = ["derive"]}
scale-info = {workspace = true, features = ["derive"]}

# Substrate dependencies
frame-system.workspace = true
frame-support.workspace = true
sp-runtime.workspace = true
sp-core.workspace = true
sp-trie.workspace = true
sp-std.workspace = true

[features]
default = ["std"]
std = [
"did/std",
"pallet-dip-consumer/std",
"parity-scale-codec/std",
"scale-info/std",
"frame-system/std",
"frame-support/std",
"sp-runtime/std",
"sp-core/std",
"sp-trie/std",
"sp-std/std"
]
runtime-benchmarks = []
Loading

0 comments on commit 36b2f45

Please sign in to comment.