Skip to content

Latest commit

 

History

History
768 lines (669 loc) · 32.2 KB

README.md

File metadata and controls

768 lines (669 loc) · 32.2 KB

EW DID Library v0.1

Disclaimer

The EW-DID library is not ready for production grade applications.

Introduction

The EW-DID library implementation confirms to the requirements specified in the DID Specification published by the W3C Credential Community Group. The aim of the library is to :

  • provide an abstraction layer to manage and interact with DIDs and Verifiable Claims on Energy Web Chain
  • enable EW Member organisation to adopt or/and implement different DID methods

EW DID Packages

Description

Package Description
@energyweb/ew-did-registry/did-registry entry-point package for end-users.
@energyweb/ew-did-registry/did-document exposes CRUD operations for DID Documents that are based on DID W3C Specification. Main class is an abstract factory that allows to work with lite or full implementations of CRUD.
@energyweb/ew-did-registry/did-ethr-resolver connects did-document’s CRUD methods with a particular blockchain implementation of DID specification.
@energyweb/ew-did-registry/did-interface-resolver provides an interface to implement DID method.
@energyweb/ew-did-registry/claims manages Public, Private and Proof claims.
@energyweb/ew-did-registry/keys provides key management and asymmetric cryptography.
@energyweb/ew-did-registry/jwt exposes methods to sign, verify, encode, and decode JWTs.

Package Diagram

Using EW DID

Setup

Install root dependencies

npm install

Install dependencies and link packages

npm run setup

Compile packages

npm run build

Test

Install docker

npm run test-rpc

Design Goals

The (other) ongoing Decentralised Identities implementation are only supporting a single DID method. The DLT/Blockchain specific DID method implementations are still evolving. Application developers might find different DID methods relevant to their application needs. With the help of EW DID, we are trying to standardise the interaction with DIDs while offering a variety of underlying DID method implementations by EWF Members. EW DID library aims to have reusable components related to DID operations and to reduce the development efforts associated with using DID in dApps. There are three design goals for the EW DID library:

  1. Implementing the W3C's DID V1.0 specification
  2. Adhering to the W3C's VC specification
  3. Ability to implement support for different DID methods on EWC

Decentralised Identifiers (DID)

Decentralized Identifiers (DIDs) are a new type of identifiers for verifiable, decentralized digital identity. These new identifiers are designed to enable the controller of a DID to prove control over it and to be implemented independently of any centralized registry, identity provider, or certificate authority. DIDs are URLs that relate a DID subject to means for trustable interactions with that subject. DIDs resolve to DID Documents — simple documents that describe how to use that specific DID. Each DID Document may express cryptographic material, verification methods, and/or service endpoints. These provide a set of mechanisms which enable a DID controller to prove control of the DID. Service endpoints enable trusted interactions with the DID subject.

EW DID Scheme

Format

EW DID utilizes the identifier format defined in DID Specification. The ABNF definition for EW DID identifier has the following syntax:

did                = "did:" method ":" specific-idstring ;
method             = "ethr"
specific-idstring  = idstring *( ":" idstring ) ;
idstring           = BASE16;

EW DID for ethr method example:

did:ethr:0xD845B41AB4837E06Aa7335E31D98c9097a064891

Currently EW DID identifier syntax does not encode the network string(e.g. ewc, volta) in the syntax. Network should be configured as show in the following section.

Setting up the network and method configuration

In order EW-DID to perform different DID operations, the resolver needs to be aware of the connection provider to Energy Web network and address of DID registry contract. This can be done by configuring the IResolverSettings instance.

The first implemented resolver is for the ERC-1056 specification. In this example we use the ERC-1056 Identity registry smart contract deployed on the Volta test net to read and write DID attributes.

import { address1056, abi1056, ProviderTypes, IResolverSettings, Resolver, IServiceEndpoint } from 'ew-did-registry/packages/did-ethr-resolver';

const NETWORK_URL = 'https://volta-rpc.energyweb.org/';

const provider = {
  uriOrInfo: ${NETWORK_URL},
  type: ProviderTypes.HTTP,
};

// construct the IResolverSettings
const resolverSettings: IResolverSettings = {
  provider,
  abi: abi1056,
  address: address1056
};

// perform a read only operation on given user did
const userDid = 'did:ethr:0xD845B41AB4837E06Aa7335E31D98c9097a064891';
const document = new DIDDocumentLite(userDid, new Resolver(provider, resolverSettings));

DID Document

A DID can be resolved to a DID document with help of a resolver. The DID document is a JSON-LD object which can be constructed by retrieving the attributes that describes:

  • owner of the DID
  • list of valid cryptographic keys
  • list of ways that can be used to authenticate
  • list of service endpoints associated with the DID Example:
{
  "@context": "https://www.w3.org/ns/did/v1",
  "id": "did:example:123456789abcdefghi",
  "authentication": [{
    //used to authenticate as did ...fghi
    "id": "did:example:123456789abcdefghi#keys-1",
    "type": "RsaVerificationKey2018",
    "controller": "did:example:123456789abcdefghi",
    "publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
  }],
  "service": [{
    // used to retrieve Verifiable Credentials associated with the DID
    "id":"did:example:123456789abcdefghi#vcs",
    "type": "VerifiableCredentialService",
    "serviceEndpoint": "https://example.com/vc/"
  }]
}

DID-Registry Package

DID Registry package wraps the different functionality around the DID and serves as a single point of entry to manage DID and claims lifecycle.

  • Instantiate DID Registry
const userKeys = new Keys({
    privateKey: '813e864ffa199f3cd38d8dcf2b097a2e2b226e000f3a05267eee23d0da7086f4',
    publicKey: '029462cf4b9ece1f84b600e3d924641aa359f068f1876cbf08b1b345e4c9831f23',
  });
  
const userDid = 'did:ethr:0x7551eD4be4eFd75E602189E9d59af448A564AB3a';

const NETWORK_URL = 'https://volta-rpc.energyweb.org/';

const provider = {
  uriOrInfo: ${NETWORK_URL},
  type: ProviderTypes.HTTP,
};

// initialise the DIDRegistry with keys, a configured Resolver instance and a DidStore
const didReg = new DIDRegistry(userKeys, userDid, new Resolver(provider, resolverSettings), new DidStore(ipfsUrl));

// create a claimsCreator for User
const userClaims = didReg.claims.createClaimsUser();

//construct the claim payload for public claim
const claimData = {
      name: 'Tesla Model 3',
      capacity: '10',
      price: '500',
    }; 

// create a signed claim token with claim payload/data
const claimToken = await userClaims.createPublicClaim(claimData);

DID-DOCUMENT Package

The did-document package functionality is in line with did-reg package. The Interfaces provides the client with a factory to create DID Document objects, which in turn expose DID CRUD operations in the full version, as well as read operations for lite version. Interfaces are created by conforming to W3C DIDs v1.0 standard. The goal of the Resolver interface is to provide flexibility to the user to define his own implementation of the Resolver for the required DID method on Energy Web chain. Currently the 1056 Operator implements the CRUD behaviour required for ethr DID method which is based on ERC1056 standard.

Create

To create a DID,

const ownerAddress = '0xed6011BBaB3B98cF955ff271F52B12B94BF9fD28';
const did = `did:ethr:${ownerAddress}`;

const keys = new Keys({
    privateKey: '0b4e103fe261142b716fc5c055edf1e70d4665080395dbe5992af03235f9e511',
    publicKey: '02963497c702612b675707c0757e82b93df912261cd06f6a51e6c5419ac1aa9bcc',
  });

const registrySettings = { 
  method: Methods.Erc1056,
  abi: ethrReg.abi,
  address: registry
};

const providerSettings = {
  type: ProviderTypes.HTTP,
  uriOrInfo: 'https://volta-rpc.energyweb.org',
};

// instantiate the operator with the signer and configured Resolver settings
const signer = EwSigner.fromPrivateKey(keys.privateKey, providerSettings);
const operator = new Operator(owner, registrySettings);

//create the DIDDocumentFull instance
const document = new DIDDocumentFull(did, operator);

// For the 1056 implementation this will only add public key the user's DID Document. There is no blockchain transaction involved
const created = await document.create();

Read

Using the EW DID resolver we can read the whole DID document. You can read specific attribute using the DIDDocument instance.

Fetching the whole DID Document

import { Resolver } from '@ew-did-registry/did-ethr-resolver';

// did of the user
const did = 'did:ewc:0xe2e457aB987BEd9AbdEE9410FC985E46e28a3947';

// Read the whole document 
const document: IDidDocument = Resolver.read(did);

// Check whether it's a valid DID document 

Reading an attribute

const didDocumentLite: IDIDDocumentLite;

// initialise the DIDDocument instance with configured resolver
didDocumentLite = new DIDDocumentLite(did, resolver);

//read the public key of the user from DID document
const publicKey = await didDocumentLite.read(Attributes.publicKey, 'Secp256k1VerificationKey');

Update

Adding a valid public key for verfication.

// add an attribute to DID Document of the user
const updated = await document.update(
  DIDAttribute.PublicKey,
  {
    type: PubKeyType.VerificationKey2018,
    algo: Algorithms.ED25519,
    encoding: Encoding.HEX,
    value: new Keys().publicKey,
  },
  validity,
);

Adding an authentication method

const delegate = new Wallet(new Keys().privateKey);

const updated = await document.update(
  DIDAttribute.Authenticate,
  {
    type: PubKeyType.SignatureAuthentication2018,
    algo: Algorithms.ED25519,
    encoding: Encoding.HEX,
    delegate: delegate.address,
  },
  validity,
);

Adding a service endpoint for a claim

// serviceEndPoint of the claim to be added
const endpoint = 'https://claimstore.energyweb.org/gba42asdf';

//add the service endpoint
const updated = await document.update(
  DIDAttribute.ServicePoint,
  {
    type: PubKeyType.VerificationKey2018,
    value: endpoint,
  },
  validity,
);

Delete/Revoke

Currently revocation functionality exposed only through IOperator interface. In future, it will available through the IDIDDocument interface

Revocation of the public key

//publicKey to be revoked
const attribute = DIDAttribute.PublicKey;

const updateData: IUpdateData = {
      algo: Algorithms.ED25519,
      type: PubKeyType.VerificationKey2018,
      encoding: Encoding.HEX,
      value: keysAttribute.publicKey,
    };
const revoked = await operator.revokeAttribute(did, attribute, updateData);

Revocation of the authentication method

//authentication method to be Revoked
const delegateDid = `did:ewc:${delegate.address}`;

//revoke the authentication method
const revoked = await operator.revokeDelegate(did, PubKeyType.VerificationKey2018, delegateDid);

Revocation of the service point

//serviceEndpoint to be revoked
const endpoint = 'https://claimstore.energyweb.org/gba42asdf';

const revoked = await operator.revokeAttribute(
    did, 
    DIDAttribute.ServicePoint,
   {
    type: PubKeyType.VerificationKey2018,
    value: endpoint,
  }
);

Revoking all attributes

// revokes attributes related to authentication and service endpoints
const deactivated = await document.deactivate();

Resolver Package

1056 Resolver

Resolver is the implementation of ERC1056 standard. It only exposes the Read functionality of the DID document. The class is implemented with caching. Recurring calls will execute significantly faster.

The design goal of EW DID is to support different DID methods on Energy Web Chain. The resolver currently has the support for ERC1056 standard (ethr DID method). In future, we will have support for ERC725 standard which enables erc725 DID method.

  • Importing required modules
import { Resolver, DelegateTypes } from '@ew-did-registry/did-ethr-resolver';
  • Reading the DID Document for particular id
    const NETWORK_URL = 'https://volta-rpc.energyweb.org/';
    const provider = {
      uriOrInfo: ${NETWORK_URL},
      type: ProviderTypes.HTTP,
    };
    const resolver = new Resolver(provider, resolverSettings);
    const did = 'did:ethr:0xe2e457aB987BEd9AbdEE9410FC985E46e28a3947';
    const didDocument = await resolver.read(did);
  • Reading the current owner of the did

This method doesn't require full document fetching. Returns did of the current owner.

    const did = 'did:ethr:0xe2e457aB987BEd9AbdEE9410FC985E46e28a3947';
    const owner = await resolver.identityOwner(did);
  • Checking if delegate is present in the DID Document

This read doesn't require full document fetching. Returns boolean if the delegate is present.

    const did = 'did:ethr:0xe2e457aB987BEd9AbdEE9410FC985E46e28a3947';
    const didDelegate = 'did:ewc:0xe2e457aods7BEd9AbdEE9410xt985E46e28a3947';
    const validDelegate = await resolver.validDelegate(did, DelegateTypes.verification, didDelegate);

Claims Package

The claims package provides an interface to manage public and private claims in a straightforward manner. It abstracts the claim lifecycle, that currently consists of the following stages:

  • creation and issuance of public and private claims
  • creation of proofs for the issued claims and verification thereof

Public Claims

  • Importing required modules
import {
  IResolver, IOperator, DIDAttribute, IUpdateData, PubKeyType, Algorithms, Encoding,
} from '@ew-did-registry/did-resolver-interface';
import { Keys } from '@ew-did-registry/keys';
import { Methods } from '@ew-did-registry/did';
import { IClaim } from '@ew-did-registry/claims';
import { DIDDocumentFull } from '@ew-did-registry/did-document';
import DIDRegistry from '@ew-did-registry/did-registry';
  • Creating identities based on their roles

User is the claims subject

  const userKeys = new Keys({
    privateKey: '42f9eb48de908412da91f0e7b6d8f987db91cbf7bf2639c53394b746d91d2382',
    publicKey: '0391feb03b9fadd2dfb9dfe7d3c53cd4a64094bd7ffd19beb8c46efbeaf2724f32',
  });
  const userAddress = '0xE7804Cf7c346E76D3BA88da639F3c15c2b2AE4a5';
  const userDid = `did:${Methods.Erc1056}:${userAddress}` ;

Operator - is an interface responsible for DID document updating

  const providerSettings = {
    type: ProviderTypes.HTTP,
    uriOrInfo: 'https://volta-rpc.energyweb.org/',
  }
  const signer = EwSigner.fromPrivateKey(userKeys.privateKey, providerSettings);
  const userOperator = new OperuserOator(signer, resolverSettings);

Before using DID document it needs to be initialized. During initialization, the document stores the user's public key associated with its etherum address

  await userOperator.create();

DIDRegistry - main interface for working with claims and DID documents

  const user = new DIDRegistry(userKeys, userDid, new Resolver(provider, resolverSettings), new DidStore(ipfsUrl));

Claims creator is represented by IClaimsUser

  const userClaims: IClaimsUser = user.claims.createClaimsUser();

Same flow for issuer. Issuer checks claim data and issue token, which can be stored and verified

  const issuerKeys = new Keys({
    privateKey: '945d90baf66123693be97edff663d5c54f5d517d40928a9c0caa37dba3a0b042',
    publicKey: '0232c391f52ff6c63e1ffdfa6921822aee895d2a21bb28a71370404b05960c9263',
  }); 
  const issuerAddress = '0xddCe879DE01391176a8527681f63A7D3FCA2901B'; 
  const issuerDid = `did:${Methods.Erc1056}:${issuerAddress}` ; 
  const issuer = new DIDRegistry(issuerKeys, issuerDid, new Resolver(provider, resolverSettings), new DidStore(ipfsUrl)); 
  const issuerClaims = issuer.claims.createClaimsIssuer();

Same flow for verifier

  const verifierKeys = new Keys({
    privateKey: '37cd773efb8cd99b0f509ec118df8e9c6d6e5e22b214012a76be215f77250b9e',
    publicKey: '02335325b9d16aa046ea7275537d9aced84ed3683a7969db5f836b0e6d62770d1e',
  }); 
  const verifierAddress = '0x6C30b191A96EeE014Eb06227D50e9FB3CeAbeafd'; 
  const verifierDid = `did:${Methods.Erc1056}:${verifierAddress}` ; 
  const verifier = new DIDRegistry(verifierKeys, verifierDid, new Resolver(provider, resolverSettings), new DidStore(ipfsUrl)); 

The time interval during which the corresponding record in the DID document will be valid

  const validity = 5 * 60 * 1000;
  • Claim creation
  const claimData = {
      name: 'Tesla Model 3',
      capacity: '10',
      price: '500',
    }; 
    const token = await userClaims.createPublicClaim(claimData);
  • Claim issuance
  const issuedToken = await issuerClaims.issuePublicClaim(token);
  • Verification of issued claim and adding issuer to delegates

'verifyPublicClaim' checks if the claim has the correct payload and also adds delegate to the smart contract

  const verified = await userClaims.verifyPublicClaim(issuedToken); 
  expect(verified).is.true;
  };
  • Verifier checks if the presented token is valid

'verifyPublicProof' checks the signature on the claim, as well as whether the delegate is valid for the DID

  const verified = await claimsUser.verifyPublicProof(issuedToken);
  expect(verified).to.be.true;

An IDIDDocumentLite interface is used to read a document

  const userLightDoc: IDIDDocument = user.documentFactory.createLite(new Resolver(provider, resolverSettings)); 
  await userLightDoc.read(userDid); 
  let document = userLigthDoc.didDocument;

An IDIDDocumentFull interface is used to update a document

  const providerSettings = {
    type: ProviderTypes.HTTP,
    uriOrInfo: 'https://volta-rpc.energyweb.org',
  };
  const signer = EwSigner.fromPrivateKey(userKeys.privateKey, providerSettings);
  const signer = IdentitOwner.fromPrivateKeySigner(signer);
  const DIDOperator = new Operator(signer, registrySettings);

  const userFullDoc: IDIDDocumentFull = user.documentFactory.createFull(DIDOperator);
  expect(userFullDoc).instanceOf(DIDDocumentFull);
  await userFullDoc.update(DIDAttribute.Authenticate, updateData, validity);
});

Private Claims

  • Importing required modules
  import { expect } from 'chai';
  import {
    Resolver, Operator, DIDAttribute, IUpdateData, PubKeyType, Algorithms, Encoding
  } from '@ew-did-registry/did-resolver-interface';
  import { Keys } from '@ew-did-registry/keys';
  import { Methods } from '@ew-did-registry/did';
  import { IClaim } from '@ew-did-registry/claims';
  import { DIDDocumentFull } from '@ew-did-registry/did-document';
  import DIDRegistry from '@ew-did-registry/did-registry';
  • Creating identities based on their roles

User is the claims subject

  const userKeys = new Keys({
    privateKey: '42f9eb48de908412da91f0e7b6d8f987db91cbf7bf2639c53394b746d91d2382',
    publicKey: '0391feb03b9fadd2dfb9dfe7d3c53cd4a64094bd7ffd19beb8c46efbeaf2724f32',
  });
  const userAddress = '0xE7804Cf7c346E76D3BA88da639F3c15c2b2AE4a5';
  const userDid = `did:${Methods.Erc1056}:${userAddress}` ;

Operator - an interface responsible for DID document updating

  const providerSettings = {
    type: ProviderTypes.HTTP,
    uriOrInfo: 'https://volta-rpc.energyweb.org',
  };
  const signer = EwSigner.fromPrivateKey(userKeys.privateKey, providerSettings);
  const userOperator = new Operator(signer, registrySettings);

Before using DID document it needs to be initialized. During initialization, the document stores the user's public key associated with its Etherum address. Each document update costs a Volts, therefore make sure that there are enough funds on the account

  await userOperator.create();

DIDRegistry - main interface for working with claims and DID documents

  const user = new DIDRegistry(userKeys, userDid, new Resolver(provider, resolverSettings), new DidStore(ipfsUrl));

Claims creator is represented by IClaimsUser

  const userClaims: IClaimsUser = user.claims.createClaimsUser();

Same flow for issuer. Issuer checks claim data and issue token, which can be stored and verified

  const issuerKeys = new Keys({
    privateKey: '945d90baf66123693be97edff663d5c54f5d517d40928a9c0caa37dba3a0b042',
    publicKey: '0232c391f52ff6c63e1ffdfa6921822aee895d2a21bb28a71370404b05960c9263,
  }); 
  const issuerAddress = '0xddCe879DE01391176a8527681f63A7D3FCA2901B'; 
  const issuerDid = `did:${Methods.Erc1056}:${issuerAddress}` ; 
  const issuer = new DIDRegistry(issuerKeys, issuerDid, new Resolver(provider, resolverSettings), DidStore(ipfsUrl)); 
  const issuerClaims = issuer.claims.createClaimsIssuer();

Same flow for verifier

  const verifierKeys = new Keys({
    privateKey: '37cd773efb8cd99b0f509ec118df8e9c6d6e5e22b214012a76be215f77250b9e',
    publicKey: '02335325b9d16aa046ea7275537d9aced84ed3683a7969db5f836b0e6d62770d1e',
  }); 
  const verifierAddress = '0x6C30b191A96EeE014Eb06227D50e9FB3CeAbeafd'; 
  const verifierDid = `did:${Methods.Erc1056}:${verifierAddress}` ; 
  const verifier = new DIDRegistry(verifierKeys, verifierDid, new Resolver(provider, resolverSettings), new DidStore(ipfsUrl));

The time interval during which the corresponding record in the DID document will be valid. Validity is stored in milliseconds, hence 5 minutes are represented in the example below

  const validity = 5 * 60 * 1000;
  • Claim creation
  const claimData = {
    secret: '123',
    notSecret: 'string',
  };
  const { token, saltedFields } = await userClaims.createPrivateClaim(claimData, issuerDid);

Private claim will contain private user data encoded with issuer key. Salted fields will be used to verify issued claim and to create proof claim

  • Claim issuance

Issuer encodes private user data and then hashes it

  const issuedToken = await issuerClaims.issuePrivateClaim(token);
  • Verification of issued claim and adding issuer to delegates
  const verified = await userClaims.verifyPrivateClaim(issuedToken, saltedFields); 
  expect(verified).is.true;
  const claim: IClaim = userClaims.jwt.decode(issuedToken) as IClaim; 
  expect(claim.did).equal(userDid); 
  expect(claim.signer).equal(issuerDid); 
  expect(claim.claimData).deep.equal(claimData); 
  const updateData: IUpdateData = {
    algo: Algorithms.Secp256k1,
    type: PubKeyType.VerificationKey2018,
    encoding: Encoding.HEX,
    delegate: issuerAddress,
  };

An IDIDDocumetLite interface is used to read a document

  const userLightDoc: IDIDDocumentLite = user.documentFactory.createLite(new Resolver(provider, resolverSettings)); 
  await userLightDoc.read(userDid); 
  let document = userLightDoc.didDocument;

An IDIDDocumentFull interface is used to update a document

  // Instantiate the DIDOperator
  const providerSettings = {
    type: ProviderTypes.HTTP,
    uriOrInfo: 'https://volta-rpc.energyweb.org',
  };
  const signer = EwSigner.fromPrivateKey(userKeys.privateKey, providerSettings);
  const signer = IdentitOwner.fromPrivateKeySigner(signer);
  const DIDOperator = new Operator(signer, registrySettings);

  // Use DIDOperator to enable creation and update of the document
  const userFullDoc: IDIDDocumentFull = user.documentFactory.createFull(DIDOperator);
  expect(userFullDoc).instanceOf(DIDDocumentFull);
  await userFullDoc.update(DIDAttribute.Authenticate, updateData, validity);
  await userLigthDoc.read(userDid);
  document = userLigthDoc.didDocument;
  const expectedPkId = `${userDid}#delegate-${PubKeyType.VerificationKey2018}-${issuerAddress}`;
  expect(document.publicKey.find((pk) => pk.id === expectedPkId)).to.not.undefined;

Application saves issued token

  • User proves his ownership of private data
  const claimUrl = 'http://test.service.com';
  const encryptedSaltedFields: IProofData = {};
  let counter = 0;
  Object.entries(saltedFields).forEach(([key, value]) => {
    if (counter % 2 === 0) {
      encryptedSaltedFields[key] = {
        value,
        encrypted: true,
      };
    } else {
      encryptedSaltedFields[key] = {
        value,
        encrypted: false,
      };
    }
    // eslint-disable-next-line no-plusplus
    counter++;
  });
  const proofToken = await userClaims.createProofClaim(claimUrl, encryptedSaltedFields);

Application loads issued token from claimUrl = 'http://claim.url' and cryptographycally matches it with proof token

  verified = await verifier.claims.createClaimsVerifier().verifyPrivateProof(proofToken, issuedToken);
  expect(verified).is.true;

Keys Package

The keys package provides a clean and simple interface for the client and exposes cryptographic operations based on asymmetric cryptography for secp256k1 ECDSA.

// If you don't have a key pair you can generate new Key Pair
const keys = Keys.generateKeyPair(); // builder

// If you have a private key you can instantiate the Keys with private key
const keys = new Keys({ privateKey: keys.privateKey });

// If you have a public key you can Instantiate the Keys with public key
const keys = new Keys({ publicKey: keys.publicKey });

// Sign and verify data. 
const data = "test";
const signature = keys.sign(data); // Doesn't work if you initiate the Keys with public key.
console.log(keys.verify(data, signature)); // true

JWT Package

The JWT package is internally consumed by the Claims package to perform necessary operations on JWTs.

// Initiate the JWT implementation
const jwt = new IJWT(keyPair);

// Provide digitally signed JWT using ECDSA using P-256 curve and SHA-256 hash algorithm
// Various options can be specified, including Token expiration
// Returns encdoded token
try {
  const token = await jwt.sign(payload, { algorithm: 'ES256' });
} catch(e) {
  console.log(e);
}

// Siganture verification; options can be specified 
// Returns decoded payload, if signature is valid. Throws error otherwise
try {
  const decoded = await jwt.verify(token, publicKey);
} catch(e) {
  console.log(e);
}

// Decoding JWT without verifying the signature. This is require to retrieve DID of the subject
// Returns decoded object, which consists of header and payload
// If "complete" option is default(false), only payload is returned
// "json" options forces JSON.parse on the payload even if the header doesn't contain "typ":"JWT"
const decoded = jwt.decode(token, {complete: true});
console.log(decoded.header);
console.log(decoded.payload.did);

DID Resolver Interface

EW-DID library has a design goal to support different DID methods. did-document allows management of keys, authorisation, delegation and service endpoints in standardised way. In the practical scenario, the CRUD behaviour of the did-document needs to be specific to the DID method's underlying implementation. EW-DID aims to handle this through the DID method specific resolver implementation.

did-resolver-interface defines the contract required for CRUD behaviour of the did-document. did-ethr-resolver provides a reference implementation of ERC 1056 standard.

Class Diagram

Pseudo example of implementation

// MyResolver - Implement the read only behaviour for your DID Method
class Resolver implements IResolver{
        
    read(){
    // return the whole DID Document
    }
    
    readAttribute(){
    // read an attribute as per did method requirement 
    }
    
    valiDelegate(){
    //validate a delegate as per did method requirement
    }

}

// MyResolver - Implement the update and revoke behaviour for your DID Method
class Operator extends Resolver implements IOperator {

    create(){
    //create specific to did method
    }

    update(){
    //update specific to did method
    }
    deactivate(){
    //deactivate specific to did method
    }
    revokeDelegate(){
    //revokeDelegate specific to did method
    }
    
    revokeAttribute(){
    //revoke attribute specific to did method
    }

}

Future Work

Development

Publishing

Push to development will trigger canary publishing. To publish latest stable version merge PR from development to master To bump version commit message must conform to conventional commits specification