# Certification
Certification is the process of converting digital asset metadata into cryptographic artifacts, which can be used to verify the authenticity of metadata.
# Installation
We recommend you employ the certification module as an NPM package in your application.
$ npm i --save @0xcert/cert
On our official open-source GitHub repository, we also host a compiled and minimized JavaScript files that can be directly implemented into your application or website. Please also refer to the API Reference section to learn more about certification.
# Cert(options)
A class
which allows for creating and managing certification artifacts.
Arguments
Argument | Description |
---|---|
schema | [required] An object representing the JSON Schema definition. |
hasher | An asynchronous or synchronous function which accepts a value, path, and Merkle tree position, and returns value hash. By default, the value is converted into a SHA256 hash. |
noncer | An asynchronous or synchronous function which accepts value path and returns a nonce. By default, the value path is converted into a SHA256 hash. |
Usage
import { Cert } from '@0xcert/cert';
const schema = {
properties: {
'name': {
type: 'string',
},
},
type: 'object',
};
const cert = new Cert({ schema });
# calculate(metadata, evidence)
An asynchronous
class instance function
which returns asset imprint when all the provided metadata
is correctly included in the evidence
object. Note that custom metadata properties that are not described by the asset schema will always be ignored and will thus always pass.
Arguments:
Argument | Description |
---|---|
metadata | [required] An object with asset information which follows schema definition. |
evidence | [required] An object which describes and prooves metadata object. |
Result:
A string
representing asset imprint or null
when metadata doesn't match.
Example:
// arbitrary data
const evidence = {
$schema: 'https://conventions.0xcert.org/87-asset-evidence.json',
data: [...],
};
const metadata = {
name: 'John',
};
// calculate imprint
const imprint = await cert.calculate(metadata, evidence);
# disclose(metadata, paths)
An asynchronous
class instance function
which generates an evidence
for the subset of the metadata. The selected properties are provided as object paths
.
TIP
It's a good practice to always include $schema
and $evidence
properties in the metadata object.
Arguments:
Argument | Description |
---|---|
metadata | [required] An object with asset information which follows schema definition. |
paths | [required] An array of string s representing JSON key paths. |
Result:
An array
of proofs.
Example:
// arbitrary data
const metadata = {
name: 'John',
};
const paths = [
['name'],
];
// generate evidence
const evidence = await cert.disclose(metadata, paths);
# expose(metadata, paths)
A synchronous
class instance function
which creates a new object from metadata
with only the keys matching the provided paths
.
TIP
It's a good practice to always include $schema
and $evidence
properties in the metadata object.
Arguments:
Argument | Description |
---|---|
metadata | [required] An object with asset information which follows schema definition. |
paths | [required] An array of string s representing JSON key paths. |
Result:
Truncated metadata object with selected keys.
Example:
// arbitrary data
const metadata = {
name: 'John',
age: 36,
};
const paths = [
['name'],
];
// generate subset of metadata
const metadataSubset = await cert.expose(metadata, paths);
# identify(normalize)
An asynchronous
class instance function
which calculates the schema ID.
Arguments:
Argument | Description |
---|---|
normalize | A boolean which can disable object structure normalization (true by default). |
Result:
A string
representing schema ID.
Example:
// calculate schema ID
const schemaId = await cert.identify();
# imprint(metadata)
An asynchronous
class instance function
which generates asset imprint for the provided metadata
.
WARNING
Please note that an imprint can sometimes be prepended with 0x
. You should manually remove this before passing it to the 0xcert Framework.
Arguments:
Argument | Description |
---|---|
metadata | [required] An object with asset information which follows schema definition. |
Result:
A string
representing asset imprint.
Example:
// arbitrary data
const metadata = {
name: 'John',
};
// generate imprint
const imprint = await cert.imprint(metadata);
# notarize(metadata)
An asynchronous
class instance function
which generates a full object metadata certification evidence that is needed to verify any key of the provided metadata
object.
TIP
It's a good practice to always include $schema
and $evidence
properties in the metadata object.
Arguments:
Argument | Description |
---|---|
metadata | [required] An object with asset information which follows schema definition. |
Result:
An array
of proofs.
Example:
// arbitrary data
const metadata = {
name: 'John',
};
// generate proofs
const evidence = await cert.notarize(metadata);
# Asset evidence
Asset evidence represents a JSON object which holds information of the disclosed metadata and is needed when verifying asset metadata.
Object:
Key | Description |
---|---|
$schema | URL to JSON schema definition. |
data.$.nodes | An array of binary tree hashes. |
data.$.nodes.$.index | An integer number representing the hash index in a binary tree. |
data.$.nodes.$.hash | A string representing a hash node in a binary tree. |
data.$.path | An array of strings and numbers representing the JSON object key path. |
data.$.values | An array of binary tree values. |
data.$.values.$.index | An integer number representing the value index in a binary tree. |
data.$.values.$.value | A string representing a value in a binary tree. |
data.$.values.$.nonce | A string representing a secret for calculating merkle leaf. |
Example:
const evidence = {
$schema: 'https://conventions.0xcert.org/87-asset-evidence.json',
data: [
{
path: [],
nodes: [
{
index: 0,
hash: '7c8238509ade64f39e13b97eeeaca13b72e833cbf10db5b05dff43a7e22abce1',
},
...
],
values: [
{
index: 0,
value: 'John',
nonce: '5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9',
},
...
],
},
...
],
};
# @0xcert/conventions
This module provides interfaces for all the confirmed application data conventions.
Yes, the 0xcert Conventions are simply JSON objects, and you could create them yourself. But we have created this simple module to bring you type-safe TypeScript goodness.
Issue | Id | Description |
---|---|---|
xcert | e1823a17bb5dd039e2aff7d997223efed50dbeebb0fcb60cf7bba2a9b6f90140 | JSON Schema definition describing asset schema capabilities. |
86 | 9a7531dd7ac3ed4e3d2fa61d20a825cfd4be7f3e6bc540c82d9dc94b526c5123 | Basic asset data schema. |
87 | 02edbb4f2d07017d0ae9148af683a63d4b86d0d357a5019dd794d377a1523e9e | Asset evidence data schema. |
88 | cd3d7fce94669724f964061572f42ae0391996b0e348c7431251f9ab1bab0f49 | Schema describing digital collectible item. |
Example:
import { Schema86, schema86 } from '@0xcert/conventions';
// Schema88: Typescript interface
// schema86: JSON object
We incentivize the community to agree on standard schema conventions. Schema conventions should be proposed and included in the official 0xcert repository on GitHub. Please propose a new convention by opening a GitHub issue.
# 0xcert algorithms
The 0xcert Protocol provides two hashing algorithms. The Asset Imprint Hashing algorithm (AIH) is used for generating asset fingerprints, and the Asset Schema Hashing algorithm (ASH) generates a schema unique identifier.
# Asset Imprint Hashing algorithm (AIH)
The AIH algorithm uses Merkle tree and SHA256 cryptographic concepts to transform the JSON metadata object into cryptographic imprint
and evidence
object.
The artifacts are created based on several rules:
- Only the properties defined by the asset schema are included in the certification process.
- Each object is flattened into an array of values based on alphabetically ordered key names.
- Each value is stringified and hashed through the
sha256
hash function. - An autogenerated
nonce
is added to each hashed value. - Nonced values are hashed through
sha256
hash function. - Each array is hashed to a root hash of a binary tree.
- The last value of each array is an empty string.
- Binary tree structure details are pushed to
evidence
object for each property path that holds an object. - The root hash of the binary tree of the root object represents an
imprint
.
Let's see how this works in practice with a simple imprint. We will be using a static nonces @
so you can easily follow the steps.
const metadata = {
id: 'A',
education: {
skills: ['C', 'D'],
degree: 'E',
},
};
const evidence = {
'$schema': 'https://conventions.0xcert.org/87-asset-evidence.json',
'data': [],
};
Processing starts at leaves. The process will follow the rules described earlier and will start by calculating the binary tree of the evidence.skills
property. The following graph illustrates the hashing mechanism where (v
= value, h
= hash, r
= nonce, n
= node; v0
= C, v1
= D).
n0
|
---------
| |
n1 n2
| |
|-----| |
h0 r0 |
| |
v0 |
---------
| |
n3 n4
| |
|-----| |
h1 r1 r2
|
v1
The snippet below shows how the result for the evidence.skills
property should look like.
const metadata = {
id: 'A',
education: {
skills: '6914baafaff31f3b671257da2fbc4346f6a219d2e47010853b41d27866dbb4ae', // root hash
degree: 'E',
},
};
const evidence = {
...
'data': [
{
'path': ['education', 'skills'],
'nodes': [
{ 'index': 0, 'hash': '6914baafaff31f3b671257da2fbc4346f6a219d2e47010853b41d27866dbb4ae' }, // root hash
{ 'index': 1, 'hash': '772a324c1f21c8db4c6ab02a03cb61cf129068e02c4cdb1e65e5da1df09fedab' },
{ 'index': 2, 'hash': '92bc65cb3bae38c59c2fa47559d22dee1c73fcc3d3ef1019a43d34a7f248c910' },
{ 'index': 3, 'hash': '0ce11497c2d0b8f0f47696ef2bf3b4c87d92f0b17c7d87440025210bc8ffbe9e' },
{ 'index': 4, 'hash': 'c3641f8544d7c02f3580b07c0f9887f0c6a27ff5ab1d4a3e29caf197cfc299ae' },
],
'values': [
{ 'index': 0, 'value': 'C', 'nonce': '@' },
{ 'index': 1, 'value': 'D', 'nonce': '@' },
],
},
],
};
The process is repeated for the education
property.
const metadata = {
id: 'A',
education: '625b80bb24d3a7be910c351d800322d9d1778b509d5f3304fbe7a5dd17df4934', // root hash
};
const evidence = {
...
'data': [
{
'path': ['education'],
'nodes': [
{ 'index': 0, 'hash': '625b80bb24d3a7be910c351d800322d9d1778b509d5f3304fbe7a5dd17df4934' }, // root hash
{ 'index': 1, 'hash': '5c61b5867c3da57ccc680c0d19c91da8d2a97e1ee968b70c5dd358798e9210f2' },
{ 'index': 2, 'hash': '4c943871f0ac653a8f4b36915b0eb68db9f97491aed7edf801dc398212f354d0' },
{ 'index': 3, 'hash': 'd095436cd874aa6647547f6717046d3e9890b155a239d6d598a519d5e3504d97' },
{ 'index': 4, 'hash': 'c3641f8544d7c02f3580b07c0f9887f0c6a27ff5ab1d4a3e29caf197cfc299ae' },
],
'values': [
{ 'index': 0, 'value': 'E', 'nonce': '@' },
{ 'index': 1, 'value': '6914baafaff31f3b671257da2fbc4346f6a219d2e47010853b41d27866dbb4ae', 'nonce': '@' },
],
},
...
],
};
Finally, the process is repeated for the root properties.
const metadata = {
id: 'A',
education: '625b80bb24d3a7be910c351d800322d9d1778b509d5f3304fbe7a5dd17df4934', // root hash
};
const evidence = {
...
'data': [
{
'path': [],
'nodes': [
{ 'index': 0, 'hash': 'becc3f4e2f8069e5fd46045392235ade6dbc07a74f4473c58983e11a00c8ae78' },
{ 'index': 1, 'hash': '1e84369077b0e3c0dbb10c03cf44cff8ffe040fdef6a3de4d1b7d432a4cd92b1' },
{ 'index': 2, 'hash': '5bbe0178d6941d92b53cee28f611290e8cce4afde3d10c8237169aa4b3025e1c' },
{ 'index': 3, 'hash': '1e84369077b0e3c0dbb10c03cf44cff8ffe040fdef6a3de4d1b7d432a4cd92b1' },
{ 'index': 4, 'hash': '743d8c749207bc570957ef67b2fc205fb4a267329a743092f93c798a11e8a7cf' },
{ 'index': 5, 'hash': '094e90f0e366ed93dc96d67ec2d221e4aa79a9f27b57692e4c77eff4a93ffbac' },
{ 'index': 6, 'hash': '334cdb61ab77ca02890fbd96cee53422b6c3242a9458f8347b9eea9c276c26c5' },
{ 'index': 7, 'hash': '80a47e3b308d978595a200d5db06d514671af7081c0f358a4fb8a6d96f91cbae' },
{ 'index': 8, 'hash': 'c3641f8544d7c02f3580b07c0f9887f0c6a27ff5ab1d4a3e29caf197cfc299ae' },
],
'values': [
{ 'index': 0, 'nonce': '@' },
{ 'index': 1, 'nonce': '@' },
{ 'index': 2, 'value': '625b80bb24d3a7be910c351d800322d9d1778b509d5f3304fbe7a5dd17df4934', 'nonce': '@' },
{ 'index': 3, 'value': 'A', 'nonce': '@' },
],
},
...
],
};
The data[0].nodes[0].hash
path is the root hash which represents asset imprint and should be saved to the blockchain.
# Asset Schema Hashing algorithm (ASH)
The ASH algorithm uses SHA256 hash function to transform the JSON schema object to a cryptographic schema ID
which uniquely identifies an asset schema.
The artifact is created based on these rules:
- Schema object keys are sorted in alphabetical order.
- Schema object is stringified and hashed through the
sha256
hash function.
Let's see how this works in practice with a simple schema.
const schema = {
'$schema': 'https://conventions.0xcert.org/xcert-schema.json',
'properties': {
'$schema': {
'type': 'string',
},
'$evidence': {
'type': 'string',
},
'id': {
'type': 'string',
},
'name': {
'type': 'string',
},
'image': {
'type': 'string',
},
'power': {
'type': 'number',
},
},
'title': 'Cryptocollectible',
'type': 'object',
};
The algorithm first sorts all the keys in alphabetic order.
const schema = {
'$schema': 'https://conventions.0xcert.org/xcert-schema.json',
'properties': {
'$evidence': { ... },
'$schema': { ... },
'name': { ... },
'power': { ... },
},
'title': 'Cryptocollectible',
'type': 'object',
};
The schema is then stringified and converted to a hash.
const schemaId = '95e70b916a247557c7b1508368f1a34ec1379f23aa63e960a50e8acd639fda90';