did-sdk-js
Version:
js sdk for did and vc according to mcps did spec
617 lines • 23.9 kB
JavaScript
"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