UNPKG

@iden3/js-iden3-core

Version:

Low level API to create and manipulate iden3 Claims.

256 lines (208 loc) 6.64 kB
import { Id } from '../id'; import { Blockchain, Constants, DidMethodByte, DidMethodNetwork, DidMethod, NetworkId } from '../constants'; import { BytesHelper } from '../elemBytes'; import { DIDNetworkFlag, findBlockchainForDIDMethodByValue, findDIDMethodByValue, findNetworkIDForDIDMethodByValue } from './did-helper'; import { Parser } from './did-parser'; import { IDID, Param } from './types'; import { sha256 } from '@iden3/js-crypto'; import { encoder } from '../utils'; // DID Decentralized Identifiers (DIDs) // https://w3c.github.io/did-core/#did-syntax export class DID { method = ''; id = ''; idStrings: string[] = []; params: Param[] = []; path = ''; pathSegments: string[] = []; query = ''; fragment = ''; constructor(d?: Partial<IDID>) { if (d) { Object.assign(this, d); } } isUrl(): boolean { return ( this.params.length > 0 || !!this.path || this.pathSegments.length > 0 || !!this.query || !!this.fragment ); } string(): string { const buff = ['did:']; if (this.method) { buff.push(`${this.method}:`); } else { return ''; } if (this.id) { buff.push(this.id); } else if (this.idStrings.length) { buff.push(this.idStrings.join(':')); } else { return ''; } if (this.params.length) { for (const param of this.params) { const p = param.toString(); if (p) { buff.push(`;${p}`); } else { return ''; } } } if (this.path) { buff.push(`/${this.path}`); } else if (this.pathSegments.length) { buff.push(`/${this.pathSegments.join('/')}`); } if (this.query) { buff.push(`?${this.query}`); } if (this.fragment) { buff.push(`#${this.fragment}`); } return buff.join(''); } toJSON() { return this.string(); } static parse(s: string): DID { const parser = new Parser(s); let parserState = parser.checkLength(); while (parserState) { parserState = parserState(); } parser.out.id = parser.out.idStrings.join(':'); parser.out.path = parser.out.pathSegments.join('/'); return new DID(parser.out); } static decodePartsFromId(id: Id): { method: string; blockchain: string; networkId: string; } { const method = findDIDMethodByValue(id.bytes[0]); const blockchain = findBlockchainForDIDMethodByValue(method, id.bytes[1]); const networkId = findNetworkIDForDIDMethodByValue(method, id.bytes[1]); return { method, blockchain, networkId }; } static networkIdFromId(id: Id): string { return DID.throwIfDIDUnsupported(id).networkId; } static methodFromId(id: Id): string { return DID.throwIfDIDUnsupported(id).method; } static blockchainFromId(id: Id): string { return DID.throwIfDIDUnsupported(id).blockchain; } private static throwIfDIDUnsupported(id: Id): { method: string; blockchain: string; networkId: string; } { const { method, blockchain, networkId } = DID.decodePartsFromId(id); if (DID.isUnsupported(method, blockchain, networkId)) { throw new Error(`${Constants.ERRORS.UNKNOWN_DID_METHOD.message}: unsupported DID`); } return { method, blockchain, networkId }; } // DIDGenesisFromIdenState calculates the genesis ID from an Identity State and returns it as DID static newFromIdenState(typ: Uint8Array, state: bigint): DID { const id = Id.idGenesisFromIdenState(typ, state); return DID.parseFromId(id); } // NewDID creates a new *w3c.DID from the type and the genesis static new(typ: Uint8Array, genesis: Uint8Array): DID { return DID.parseFromId(new Id(typ, genesis)); } // ParseDIDFromID returns DID from ID static parseFromId(id: Id): DID { if (!BytesHelper.checkChecksum(id.bytes)) { throw new Error(`${Constants.ERRORS.UNSUPPORTED_ID.message}: invalid checksum`); } const { method, blockchain, networkId } = DID.throwIfDIDUnsupported(id); const didParts = [Constants.DID.DID_SCHEMA, method.toString(), blockchain.toString()]; if (networkId) { didParts.push(networkId.toString()); } didParts.push(id.string()); const didString = didParts.join(':'); const did = DID.parse(didString); return did; } static idFromDID(did: DID): Id { let id: Id; try { id = DID.getIdFromDID(did); } catch (error) { if ((error as Error).message === Constants.ERRORS.UNKNOWN_DID_METHOD.message) { return DID.idFromUnsupportedDID(did); } throw error; } return id; } static isUnsupported(method: string, blockchain: string, networkId: string): boolean { return ( method == DidMethod.Other && blockchain == Blockchain.Unknown && networkId == NetworkId.Unknown ); } static idFromUnsupportedDID(did: DID): Id { const hash = sha256(encoder.encode(did.string())); const genesis = new Uint8Array(27); const idSlice = hash.slice(hash.length - Constants.GENESIS_LENGTH); for (let i = 0; i < genesis.length; i++) { genesis[i] = idSlice[i] ?? 0; } const flg = new DIDNetworkFlag(Blockchain.Unknown, NetworkId.Unknown); const tp = Uint8Array.from([ DidMethodByte[DidMethod.Other], DidMethodNetwork[DidMethod.Other][flg.toString()] ]); return new Id(tp, genesis); } private static getIdFromDID(did: DID): Id { const method = did.method; const methodByte = DidMethodByte[method]; if (!methodByte || method === DidMethod.Other) { throw Constants.ERRORS.UNKNOWN_DID_METHOD; } if (did.idStrings.length > 3 || did.idStrings.length < 2) { throw new Error(`${Constants.ERRORS.INCORRECT_DID}: unexpected number of ID strings`); } const id = Id.fromString(did.idStrings[did.idStrings.length - 1]); if (!BytesHelper.checkChecksum(id.bytes)) { throw new Error(`${Constants.ERRORS.INCORRECT_DID}: incorrect ID checksum`); } const { method: method2, blockchain, networkId } = DID.decodePartsFromId(id); if (method2.toString() !== method.toString()) { throw new Error(`${Constants.ERRORS.INCORRECT_DID}: methods in Id and DID are different`); } if (blockchain.toString() !== did.idStrings[0]) { throw new Error(`${Constants.ERRORS.INCORRECT_DID}: blockchains in ID and DID are different`); } if (did.idStrings.length > 2 && networkId.toString() != did.idStrings[1]) { throw new Error(`${Constants.ERRORS.INCORRECT_DID}: networkIDs in Id and DID are different`); } return id; } }