Skip to content

Commit

Permalink
feat!: add JSON canonicalisation
Browse files Browse the repository at this point in the history
  • Loading branch information
ntn-x2 authored May 16, 2023
2 parents 1fa2ea0 + a1b6f06 commit b38ef0c
Showing 1 changed file with 97 additions and 41 deletions.
138 changes: 97 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,90 +1,146 @@
[![](.maintain/media/kilt-header.png)](https://kilt.io)

# KiltTransferAssetRecipientV1
# KiltTransferAssetRecipientV2

### Editors

- **Antonio Antonino** - KILT Protocol [[email protected]](mailto:[email protected])
- **Albrecht Weiche** - KILT Protocol [[email protected]](mailto:[email protected])

---

## Abstract

This document defines an extension to the service types supported in the [DID Core W3C spec][did-core-spec] by defining the `KiltTransferAssetRecipientV1` service type.
This document defines an extension to the service types supported in the [DID Core W3C spec][did-core-spec] by defining the `KiltTransferAssetRecipientV2` service type.
The goal of the endpoints of this class is to expose a collection (i.e., a list) of addresses to which assets of some class can be sent to.
For more information about the KILT DID method, please visit our [official specification][kilt-did-spec].

## Data structure
## Data Structure

A service endpoint of type `KiltTransferAssetRecipientV1` does not include any additional properties compared to what is defined within the [relative section of the official DID Core spec][did-core-spec-services].
A service endpoint of type `KiltTransferAssetRecipientV2` does not include any additional properties compared to what is defined within the [relative section of the official DID Core spec][did-core-spec-services].
Furthermore, endpoints of such type MUST include at least *one* URI for the `serviceEndpoint` property.
Each of the URIs in `serviceEndpoint`, when dereferenced, MUST return **a JSON containing an object** with a mapping from each type of asset to the list of accounts the DID subject controls for that asset, optionally with a description of each account.
Each of the URIs in `serviceEndpoint`, when dereferenced, MUST return **a JSON object** with a mapping from each type of asset to the list of accounts the DID subject controls for that asset, optionally with a description of each account.
An example of the object described is given below.

```json
{
"polkadot:411f057b9107718c9624d6aa4a3f23c1/slip44:2086": [
{
"account": "4qBSZdEoUxPVnUqbX8fjXovgtQXcHK7ZvSf56527XcDZUukq",
"description": "Treasury proposals transfers"
},
{
"account": "4oHvgA54py7SWFPpBCoubAajYrxj6xyc8yzHiAVryeAq574G",
"description": "Regular transfers"
"polkadot:b0a8d493285c2df73290dfb7e61f870f/slip44:434": {
"EJDj2GKnx89HTzUkGW8Rk9RoYUmAJHPM8aacWFp3fi1gYUQ": {
"description": "Personal account"
}
},
"polkadot:411f057b9107718c9624d6aa4a3f23c1/slip44:2086": {
"4nvZhWv71x8reD9gq7BUGYQQVvTiThnLpTTanyru9XckaeWa": {
"description": "Council account"
},
{
"account": "4taHgf8x9U5b8oJaiYoNEh61jaHpKs9caUdattxfBRkJMHvm"
"4tMSjvHfWBNQw4tYGvkbRp7BBpwAB6S24LuMDcASYgnGnRTM" : {
"description": "Personal account"
}
],
"eip:1/slip44:60": [
{
"account": "0x8f8221AFBB33998D8584A2B05749BA73C37A938A",
},
"polkadot:91b171bb158e2d3848fa23a9f1c25182/slip44:354": {
"15BQbTH5bKH63WCXTMPxbmpnWeXKpfuTKbpDkfFLXMPvpxD3": {
"description": "Personal account"
}
},
"eip:1/slip44:60": {
"0x6b175474e89094c44da98b954eedeac495271d0f": {},
"0x8f8221AFBB33998D8584A2B05749BA73C37A938A": {
"description": "NFT sales"
},
{
"account": "0x6b175474e89094c44da98b954eedeac495271d0f"
}
]
}
}
```

Each asset is identified by its [CAIP-19 identifier][caip-19-spec].
The value of the property for each asset MUST be a set of accounts encoded according to the chain rules.
For example, for Spiritnet accounts, the account is the base58-prefixed encoding of the Spiritnet chain ID + the account public key.
For Ethereum accounts, it's the 20-byte HEX representation of the account public key, prefixed with `0x`.
Other chains have different encoding rules for accounts, and each chain defines the format and encoding logic for public keys representing accounts on those chains.
The value of the property for each asset MUST be another object with the following structure:

Hence, the example above shows a `KiltTransferAssetRecipientV1` endpoint indicating other parties that the DID subject can accept transfers of the following two assets:
* One key for each `account` that can receive assets of the specified type. The account MUST be encoded according to the rules of the chain on which the asset lives. For example, for Spiritnet accounts, the account is the base58-prefixed encoding of the Spiritnet chain ID + the account public key. For Ethereum accounts, it's the 20-byte HEX representation of the account public key, prefixed with `0x`. Other chains have different encoding rules for accounts, and each chain defines the format and encoding logic for public keys representing accounts on those chains.
* For each account, an object with the following properties:
* [OPTIONAL] `description`: The user-provided description for the specified account.

- *KILT Spiritnet tokens* sent to either of the addresses `4qBSZdEoUxPVnUqbX8fjXovgtQXcHK7ZvSf56527XcDZUukq`, `4oHvgA54py7SWFPpBCoubAajYrxj6xyc8yzHiAVryeAq574G`, or `4taHgf8x9U5b8oJaiYoNEh61jaHpKs9caUdattxfBRkJMHvm` on the Spiritnet parachain.
- *Ether tokens* sent to either of the addresses `0x8f8221AFBB33998D8584A2B05749BA73C37A938A`, or `0x6b175474e89094c44da98b954eedeac495271d0f` on the Ethereum mainnet.
The example above shows a `KiltTransferAssetRecipientV2` endpoint indicating other parties that the DID subject can accept transfers of the following four assets:

The `id` property of the endpoint MUST be the [multibase][multibase] representation of the Blake256 output calculated from the Base64 encoding of the resource dereferenced by the URIs in `serviceEndpoint`.
The multibase chosen must yield values that do not contain invalid characters as per the definition of the `id` property in the DID specification, i.e., the resulting `id` must still be a valid URI conforming to [RFC3986][rfc3986].
- *DOT tokens* sent to the address `15BQbTH5bKH63WCXTMPxbmpnWeXKpfuTKbpDkfFLXMPvpxD3` on the Polkadot relaychain.
- *KILT Spiritnet tokens* sent to either of the addresses `4tMSjvHfWBNQw4tYGvkbRp7BBpwAB6S24LuMDcASYgnGnRTM`, or `4nvZhWv71x8reD9gq7BUGYQQVvTiThnLpTTanyru9XckaeWa` on the KILT Spiritnet parachain.
- *KSM tokens* sent to the address `EJDj2GKnx89HTzUkGW8Rk9RoYUmAJHPM8aacWFp3fi1gYUQ` on the Kusama relaychain.
- *ETH non-fungible tokens* sent to the either of the addresses `0x6b175474e89094c44da98b954eedeac495271d0f`, or `0x8f8221AFBB33998D8584A2B05749BA73C37A938A` on the Ethereum mainnet.

Hence, calling `M` the multibase encoding operation of some data, `H` the Blake256 hashing, and `B` the binary representation of some information, the service `id` for a given object `O` is `M(H(B(O)))`.
### Object Canonicalization and Hashing

The `id` property of the endpoint MUST be the [multibase][multibase] representation of the Blake2b-256 output calculated from the Base64 encoding of the [canonical representation][rfc8785] of the resource dereferenced by the URIs in `serviceEndpoint`.
The multibase chosen MUST yield values that do not contain invalid characters as per the definition of the `id` property in the DID specification, i.e., the resulting `id` MUST still be a valid URI conforming to [RFC3986][rfc3986].

The retrieved resource MUST be canonicalized before being hashed, according to the [RFC 8785 specification][rfc8785].
This canonicalization step is required to ensure that two semantically-equivalent services do not hash to two different values.

Hence, calling `M` the multibase encoding operation of some data, `H` the Blake2b-256 hashing, `B` the binary representation of some information, and `N` the canonicalization step, the service `id` for a given object `O` is `M(H(B(N(O))))`.
For example, with the object `O` being the example `serviceEndpoint` shown above, and the multibase `M` being `base64urlpad`, the resulting service endpoint looks like the following:

```json
{
"id": "did:kilt:4pqDzaWi3w7TzYzGnQDyrasK6UnyNnW6JQvWRrq6r8HzNNGy#Uc1JU0UF9iDfjaRkgHCFG2Rc5jki-cuhlgbEQcjN6-g0=",
"id": "did:kilt:4pqDzaWi3w7TzYzGnQDyrasK6UnyNnW6JQvWRrq6r8HzNNGy#Uif4uWQYSXeeMLAQPNX2aEJvMEmHGkvEqcL-zZdKkRhM=",
"type": [
"KiltTransferAssetRecipientV1"
"KiltTransferAssetRecipientV2"
],
"serviceEndpoint": [
"https://ipfs.io/ipfs/QmfLCY7Jxa4rRhpQ1FK3srtyM6C5ZLk4YmXfS7nm4Ur2GG"
"https://gist.githubusercontent.com/ntn-x2/375d047e6be61d243b9cf645bc94a436/raw/f41c9f4976e09a29e6bd63f84eabdcd0f6cf2f4d/KiltTransferAssetRecipientV2-example.json"
]
}
```

## Security considerations
A Typescript snippet showing how to derive the service ID for the example document above is shown below:

```ts
import { blake2AsU8a } from '@polkadot/util-crypto'
import * as multibase from 'multibase'
import canonicalize from 'canonicalize'

const doc = `
{
"polkadot:b0a8d493285c2df73290dfb7e61f870f/slip44:434": {
"EJDj2GKnx89HTzUkGW8Rk9RoYUmAJHPM8aacWFp3fi1gYUQ": {
"description": "Personal account"
}
},
"polkadot:411f057b9107718c9624d6aa4a3f23c1/slip44:2086": {
"4nvZhWv71x8reD9gq7BUGYQQVvTiThnLpTTanyru9XckaeWa": {
"description": "Council account"
},
"4tMSjvHfWBNQw4tYGvkbRp7BBpwAB6S24LuMDcASYgnGnRTM" : {
"description": "Personal account"
}
},
"polkadot:91b171bb158e2d3848fa23a9f1c25182/slip44:354": {
"15BQbTH5bKH63WCXTMPxbmpnWeXKpfuTKbpDkfFLXMPvpxD3": {
"description": "Personal account"
}
},
"eip:1/slip44:60": {
"0x6b175474e89094c44da98b954eedeac495271d0f": {},
"0x8f8221AFBB33998D8584A2B05749BA73C37A938A": {
"description": "NFT sales"
}
}
}
`

function main() {
const jsonInput = JSON.parse(doc)
const canonicalJson = canonicalize(jsonInput)
const buffer = Buffer.from(canonicalJson as any)
const hash = blake2AsU8a(buffer)
const encoded = multibase.encode('base64urlpad', hash)
console.log(Buffer.from(encoded).toString('utf-8'))
}

main()
```

## Security Considerations

The list of addresses where the DID owner wants to receive funds must always be under the subject's control even if stored off-chain.
The list of addresses where the DID owner wants to receive funds MUST always be under the subject's control even if stored off-chain.
This ensures the authenticity and integrity of the list.
Implementations must verify that the list of addresses retrieved from the service URI can be hashed and encoded to the same value as the service `id`.
Failure to verify this condition MUST be treated as an attack either towards the DID subject or the entity willing to initiate the asset transfer, and the operation must be aborted.
Implementations MUST verify that the list of addresses retrieved from the service URI can be hashed and encoded to the same value as the service `id` after following the canonicalization and hashing steps outlined above.
Failure to verify this condition MUST be treated as an attack either towards the DID subject or the entity willing to initiate the asset transfer, and the operation MUST be aborted.

[did-core-spec]: https://www.w3.org/TR/did-core
[kilt-did-spec]: https://github.com/KILTprotocol/spec-kilt-did
Expand Down

0 comments on commit b38ef0c

Please sign in to comment.