UNPKG

did-sdk-js

Version:

js sdk for did and vc according to mcps did spec

617 lines 23.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DidDocument = exports.DIDPrivateKey = exports.DIDUrl = exports.PublicKey = exports.IDGenType = void 0; const crypto = require("../crypto"); const utils_1 = require("../utils"); const errors_1 = require("../errors"); var IDGenType; (function (IDGenType) { IDGenType["IDGenTypeBech32"] = "bech32"; IDGenType["IDGenTypeUUID"] = "uuid"; })(IDGenType = exports.IDGenType || (exports.IDGenType = {})); class PublicKey { constructor(keyId, keyType, publicKeyHex) { this.id = keyId; this.type = keyType; this.publicKeyHex = publicKeyHex; } verifySignature(signData, signValue) { return __awaiter(this, void 0, void 0, function* () { let algoType = crypto.getAlgoTypeByKeyType(this.type); let keyGenerator = crypto.keyGenerator(algoType); return yield keyGenerator.signVerify(signData, signValue, this.publicKeyHex); }); } checkParam() { if (this.type != crypto.KeyType.KeyTypeSecp256k1 && this.type != crypto.KeyType.KeyTypeSm2) { return new errors_1.SdkError("type not supported"); } if (!this.id.startsWith(DIDUrl.fragmentTag)) { return new errors_1.SdkError("id format error"); } return null; } encrypt(plainText) { return __awaiter(this, void 0, void 0, function* () { let keyGen = crypto.keyGenerator(crypto.getAlgoTypeByKeyType(this.type)); let cipherText = yield keyGen.encrypt(plainText, this.publicKeyHex); return Buffer.from(cipherText, 'hex').toString('base64'); }); } } exports.PublicKey = PublicKey; class Authentication { constructor(verifyType, publicKey) { this.publicKey = []; this.type = verifyType; this.publicKey = publicKey; } checkParam() { if (this.type != crypto.VerifyType.VerifyTypeSecp256k1 && this.type != crypto.VerifyType.VerifyTypeSm2) { return new errors_1.SdkError("type not supported"); } return null; } } class Proof { constructor(verifyType, creator, signatureValue) { this.type = verifyType; this.creator = creator; this.signatureValue = signatureValue; } } class DIDUrl { static makeKeyUrl(didStr, keyId) { return didStr + keyId; } static getFragment(url) { let subs = url.split(DIDUrl.fragmentTag); if (subs.length > 0) { return DIDUrl.fragmentTag + subs[subs.length - 1]; } return ""; } } exports.DIDUrl = DIDUrl; DIDUrl.fragmentTag = "#"; DIDUrl.paramTag = "?"; DIDUrl.pathTag = "/"; DIDUrl.fragmentKeyIdMain = DIDUrl.fragmentTag + "keys-main"; DIDUrl.fragmentKeyIdApp = DIDUrl.fragmentTag + "keys-app"; DIDUrl.fragmentServiceIdResolver = DIDUrl.fragmentTag + "resolver"; DIDUrl.fragmentKeyMain = DIDUrl.fragmentKeyIdMain; DIDUrl.fragmentKeyApp = DIDUrl.fragmentKeyIdApp; class DIDPrivateKey { constructor(keyId, algoType, privateKeyHex, publicKeyHex, mnemonic) { this.keyId = keyId; this.algo = algoType; this.privateKeyHex = privateKeyHex; this.mnemonic = mnemonic; this.publicKeyHex = publicKeyHex; if (this.publicKeyHex.length == 0 && this.privateKeyHex.length != 0 && this.algo.length != 0) { this.publicKeyHex = crypto.keyGenerator(algoType).getPublicKeyFromPrivateKey(this.privateKeyHex); } } } exports.DIDPrivateKey = DIDPrivateKey; class DidDocument { constructor() { this.context = []; this.publicKey = []; this.authentication = []; this.recovery = []; this.service = []; this.controller = []; this.proof = []; } init(id) { this.context = [DidDocument.w3cContext]; this.id = id; this.version = DidDocument.firstVersion; let curTimeStr = utils_1.Utils.dateToStr(new Date()); this.created = curTimeStr; this.updated = curTimeStr; } setVersion(ver) { this.version = ver; } versionInc() { this.version += 1; } setId(id) { this.id = id; } setCreateTime(time) { if (time == null) { this.created = utils_1.Utils.dateToStr(new Date()); } else { this.created = time; } } setUpdateTime(time) { if (time == null) { this.updated = utils_1.Utils.dateToStr(new Date()); } else { this.updated = time; } } setRevokeTime(time) { if (time == null) { this.revoked = utils_1.Utils.dateToStr(new Date()); } else { this.revoked = time; } } addPublicKey(publicKey) { if (typeof this.publicKey == "undefined") { this.publicKey = [publicKey]; } else { this.publicKey.push(publicKey); } } updatePublicKey(keyID, keyType, publicKeyHex) { let publicKey = new PublicKey(keyID, keyType, publicKeyHex); if (utils_1.Utils.isUndefined(this.publicKey)) { this.publicKey = [publicKey]; } else { let found = false; for (let i = 0; i < this.publicKey.length; i++) { if (this.publicKey[i].id == keyID) { this.publicKey[i] = publicKey; found = true; break; } } if (!found) { this.publicKey.push(publicKey); } } this.updateAuthenticationType(keyID, crypto.getVerifyTypeByKeyType(keyType)); } addRecoveryKey(keyUrl) { if (typeof this.recovery == "undefined") { this.recovery = [keyUrl]; } else { this.recovery.push(keyUrl); } } addAuthentication(auth) { if (typeof this.authentication == "undefined") { this.authentication = [auth]; } else { this.authentication.push(auth); } } updateAuthenticationType(keyID, verifyType) { for (let i = 0; i < this.authentication.length; i++) { if (this.authentication[i].publicKey.length == 1 && this.authentication[i].publicKey[0] == keyID) { this.authentication[i].type = verifyType; } } } addProof(proof) { if (typeof this.proof == "undefined") { this.proof = [proof]; } else { this.proof.push(proof); } } rmProof() { if (typeof this.proof != "undefined") { this.proof.length = 0; } } static makeIdByUUID(regionID) { let uuidStr = utils_1.Utils.genUUID(); uuidStr = uuidStr.replace(/-/g, ""); return DidDocument.didPrefix + ":" + DidDocument.didMethod + ":" + regionID + ":" + uuidStr; } static makeIdByBench32(regionID, publicKeyHex) { let address = crypto.Crypto.getBech32AddressFromPublicKey(publicKeyHex); return DidDocument.didPrefix + ":" + DidDocument.didMethod + ":" + regionID + ":" + address; } checkEmpty() { if (utils_1.Utils.isNull(this.context)) { return new errors_1.SdkError("context is null"); } if (utils_1.Utils.isNull(this.id)) { return new errors_1.SdkError("id is null"); } if (utils_1.Utils.isNull(this.proof)) { return new errors_1.SdkError("proof is null"); } if (utils_1.Utils.isNull(this.version) || this.version < DidDocument.firstVersion) { return new errors_1.SdkError("id is null"); } // revoke if (this.isRevoked()) { return null; } if (utils_1.Utils.isNull(this.publicKey)) { return new errors_1.SdkError("publickey is null"); } if (utils_1.Utils.isNull(this.authentication)) { return new errors_1.SdkError("authentication is null"); } return null; } checkPublicKey() { if (utils_1.Utils.isNull(this.publicKey)) { return new errors_1.SdkError("publickey is null"); } // check if has main public key let foundMainKey = false; for (let i in this.publicKey) { let res = this.publicKey[i].checkParam(); if (res != null) { return res; } if (this.publicKey[i].id == DIDUrl.fragmentKeyMain) { foundMainKey = true; } } if (!foundMainKey) { return new errors_1.SdkError("no main public key"); } return null; } checkAuthentication() { if (utils_1.Utils.isNull(this.authentication)) { return new errors_1.SdkError("authentication is null"); } for (let i in this.authentication) { let res = this.authentication[i].checkParam(); if (res != null) { return res; } // TODO: check public key? } return null; } checkIDFormat() { if (utils_1.Utils.isNull(this.id)) { return new errors_1.SdkError("id is null"); } let arr = this.id.split(":"); if (arr.length != 4) { return new errors_1.SdkError("id split fail"); } if (arr[0] != DidDocument.didPrefix) { return new errors_1.SdkError("did prefix err: " + arr[0]); } if (arr[1] != DidDocument.didMethod) { return new errors_1.SdkError("did method err: " + arr[1]); } return null; } checkUpdateTime() { let err = utils_1.Utils.checkTimeFormat(this.updated); if (err != null) { return new errors_1.SdkError("update time format err: " + err.message + ":" + this.created); } if (this.updated < this.created) { return new errors_1.SdkError("update time is less than creat time:" + this.updated + ":" + this.created); } return null; } checkRevokeTime() { let err = utils_1.Utils.checkTimeFormat(this.revoked); if (err != null) { return new errors_1.SdkError("revoke time format err: " + err.message + ":" + this.created); } if (this.revoked < this.created) { return new errors_1.SdkError("revoke time is less than creat time:" + this.revoked + ":" + this.created); } return null; } validateBasic() { let err = this.checkEmpty(); if (err != null) { return err; } err = this.checkIDFormat(); if (err != null) { return err; } if (this.version != DidDocument.firstVersion && this.isRevoked()) { err = this.checkRevokeTime(); if (err != null) { return err; } } err = utils_1.Utils.checkTimeFormat(this.created); if (err != null) { return new errors_1.SdkError("created time format err: " + err.message + ":" + this.created); } err = this.checkUpdateTime(); if (err != null) { return err; } err = this.checkPublicKey(); if (err != null) { return err; } err = this.checkAuthentication(); if (err != null) { return err; } return null; } static creatDID(algoType, idGenType, regionID) { return __awaiter(this, void 0, void 0, function* () { let keyGenerator = crypto.keyGenerator(algoType); let mainKey = keyGenerator.generateMnemonicAndKey(); let appKey = keyGenerator.generateMnemonicAndKey(); let id = ""; if (idGenType == IDGenType.IDGenTypeUUID) { id = DidDocument.makeIdByUUID(regionID); } else { id = DidDocument.makeIdByBench32(regionID, mainKey.publicKey); } let document = new DidDocument(); document.init(id); document.addPublicKey(new PublicKey(DIDUrl.fragmentKeyMain, crypto.getKeyTypeByAlgo(algoType), mainKey.publicKey)); document.addPublicKey(new PublicKey(DIDUrl.fragmentKeyApp, crypto.getKeyTypeByAlgo(algoType), appKey.publicKey)); document.addRecoveryKey(DIDUrl.fragmentKeyMain); document.addAuthentication(new Authentication(crypto.getVerifyTypeByAlgo(algoType), [DIDUrl.fragmentKeyApp])); let signData = document.getSignData(); let signValue = yield keyGenerator.sign(signData, mainKey.privateKey); // base64 document.addProof(new Proof(crypto.getVerifyTypeByAlgo(algoType), DIDUrl.fragmentKeyMain, signValue)); let didPrivateInfo = [new DIDPrivateKey(DIDUrl.fragmentKeyMain, algoType, mainKey.privateKey, mainKey.publicKey, mainKey.mnemonic), new DIDPrivateKey(DIDUrl.fragmentKeyApp, algoType, appKey.privateKey, appKey.publicKey, appKey.mnemonic)]; let err = document.validateBasic(); if (err != null) { throw err; } return { document, didPrivateInfo }; }); } isRevoked() { return !(utils_1.Utils.isUndefined(this.revoked) || this.revoked == ""); } resetPublicKey(algoType, keyID, mainPrivateKeyInfo) { return __awaiter(this, void 0, void 0, function* () { if (this.isRevoked()) { throw new errors_1.SdkError("did is already revoked: " + this.id); } let err = this.validateBasic(); if (err != null) { throw err; } let keyGenerator = crypto.keyGenerator(algoType); let newKey = keyGenerator.generateMnemonicAndKey(); let newDid = this.copy(); newDid.rmProof(); newDid.setUpdateTime(null); newDid.versionInc(); newDid.updatePublicKey(keyID, crypto.getKeyTypeByAlgo(algoType), newKey.publicKey); let signKeyGenerator = crypto.keyGenerator(mainPrivateKeyInfo.algo); let signData = newDid.getSignData(); let signValue = yield signKeyGenerator.sign(signData, mainPrivateKeyInfo.privateKeyHex); newDid.addProof(new Proof(crypto.getVerifyTypeByAlgo(algoType), DIDUrl.fragmentKeyMain, signValue)); let newPrivateInfo = new DIDPrivateKey(keyID, algoType, newKey.privateKey, newKey.publicKey, newKey.mnemonic); err = newDid.validateBasic(); if (err != null) { throw err; } return { document: newDid, privateInfo: newPrivateInfo }; }); } revoke(mainPrivateKeyInfo) { return __awaiter(this, void 0, void 0, function* () { if (this.isRevoked()) { throw new errors_1.SdkError("did is already revoked: " + this.id); } let err = this.validateBasic(); if (err != null) { throw err; } let keyGenerator = crypto.keyGenerator(mainPrivateKeyInfo.algo); let newDid = this.copy(); newDid.rmProof(); newDid.setUpdateTime(null); newDid.setRevokeTime(null); newDid.versionInc(); let signData = newDid.getSignData(); let signValue = yield keyGenerator.sign(signData, mainPrivateKeyInfo.privateKeyHex); newDid.addProof(new Proof(crypto.getVerifyTypeByAlgo(mainPrivateKeyInfo.algo), DIDUrl.fragmentKeyMain, signValue)); err = newDid.validateBasic(); if (err != null) { throw err; } return newDid; }); } getPublicKeyByKeyID(keyID) { for (let i = 0; i < this.publicKey.length; i++) { if (this.publicKey[i].id == keyID) { // return new PublicKey(this.publicKey[i].id, this.publicKey[i].type, this.publicKey[i].publicKeyHex) return this.publicKey[i]; } } return null; } signVerify(lastVersionDoc) { return __awaiter(this, void 0, void 0, function* () { if (utils_1.Utils.isUndefined(this.proof) || (this.proof.length == 0)) { return new errors_1.SdkError("proof is null"); } if (lastVersionDoc == null) { lastVersionDoc = this; } let signData = this.getSignData(); for (let i = 0; i < this.proof.length; i++) { let publicKey = lastVersionDoc.getPublicKeyByKeyID(this.proof[i].creator); if (publicKey == null) { return new errors_1.SdkError("get no public key for " + this.proof[i].creator); } if (!(yield publicKey.verifySignature(signData, this.proof[i].signatureValue))) { return new errors_1.SdkError("sign verify fail for pubkey " + publicKey.publicKeyHex); } } return null; }); } getSignData() { let copyDid = this.copy(); copyDid.rmProof(); return copyDid.toString(); } toString() { // this.updateDocument() // TODO: 优化context 替换方法 let str = JSON.stringify(this, utils_1.Utils.replacerTrimEmpty).replace("context", "@context"); let obj = JSON.parse(str); // sort by key obj = utils_1.Utils.sortObject(obj); return JSON.stringify(obj); } copy() { return utils_1.Utils.clone(this); } // TODO: 优化parse方法实现 static parseFrom(docStr) { docStr = docStr.replace("@context", "context"); let docOjb = JSON.parse(docStr); // // TODO: 优化context替换方法 // if (docOjb.hasOwnProperty("@context")) { // docOjb["context"] = docOjb["@context"] // delete docOjb["@context"] // } let doc = Object.assign(new DidDocument(), docOjb); // TODO: 优化 // 内部嵌套的object单独处理 if (!utils_1.Utils.isNull(doc.publicKey)) { for (let i = 0; i < doc.publicKey.length; i++) { doc.publicKey[i] = new PublicKey(doc.publicKey[i].id, doc.publicKey[i].type, doc.publicKey[i].publicKeyHex); } } if (!utils_1.Utils.isNull(doc.authentication)) { for (let i = 0; i < doc.authentication.length; i++) { doc.authentication[i] = new Authentication(doc.authentication[i].type, doc.authentication[i].publicKey); } } if (!utils_1.Utils.isNull(doc.proof)) { for (let i = 0; i < doc.proof.length; i++) { doc.proof[i] = new Proof(doc.proof[i].type, doc.proof[i].creator, doc.proof[i].signatureValue); } } return doc; } static checkNewerVersion(oldD, newD) { return __awaiter(this, void 0, void 0, function* () { let err = null; err = oldD.validateBasic(); if (err != null) { return new errors_1.SdkError("old validate basic fail: " + err.message); } err = newD.validateBasic(); if (err != null) { return new errors_1.SdkError("new validate basic fail: " + err.message); } if (oldD.version + 1 != newD.version) { return new errors_1.SdkError("new version is not inc than old version"); } if (oldD.isRevoked()) { return new errors_1.SdkError("old is already revoked"); } if (oldD.updated > newD.updated) { return new errors_1.SdkError("old date is larger than new"); } if ((yield newD.signVerify(oldD)) != null) { return new errors_1.SdkError("new version sign verify fail"); } return null; }); } static getValidAndLatestVersions(docs) { return __awaiter(this, void 0, void 0, function* () { let tmpValidVersion = []; let validVersion = []; for (let i = 0; i < docs.length; i++) { if (docs[i].version == DidDocument.firstVersion && docs[i].validateBasic() == null && (yield docs[i].signVerify(null)) == null) { tmpValidVersion = docs.slice(i); break; } } if (tmpValidVersion.length == 0) { return { valid: tmpValidVersion, latest: null }; } if (tmpValidVersion.length == 1) { return { valid: tmpValidVersion, latest: tmpValidVersion[0] }; } validVersion.push(tmpValidVersion[0]); let latestVersion = validVersion[0]; for (let i = 1; i < tmpValidVersion.length; i++) { if ((yield DidDocument.checkNewerVersion(latestVersion, tmpValidVersion[i])) == null) { latestVersion = tmpValidVersion[i]; validVersion.push(tmpValidVersion[i]); } } return { valid: validVersion, latest: latestVersion }; }); } getPublicKeyByKeyHex(pubKeyHex) { for (let i in this.publicKey) { if (this.publicKey[i].publicKeyHex == pubKeyHex) { return this.publicKey[i]; } } return null; } getPublicKeyByKeyUrl(keyUrl) { let keyFrag = DIDUrl.getFragment(keyUrl); if (keyFrag.length == 0) { return null; } for (let i in this.publicKey) { if (this.publicKey[i].id == keyFrag) { return this.publicKey[i]; } } return null; } hasAuthenticationPerm(publicKey) { for (let i in this.authentication) { for (let p in this.authentication[i].publicKey) { if (this.authentication[i].publicKey[p] == publicKey.id) { return true; } } } return false; } } exports.DidDocument = DidDocument; DidDocument.startVersion = 1; DidDocument.w3cContext = "https://w3id.org/did/v1"; DidDocument.didPrefix = "did"; DidDocument.didMethod = "mcps"; DidDocument.firstVersion = 1; //# sourceMappingURL=did.js.map