@bajetech/digitalbits-wallet-sdk
Version:
A library to make it easier to write wallets that interact with the DigitalBits blockchain
294 lines • 13.1 kB
JavaScript
import { __assign, __awaiter, __generator } from "tslib";
import { astraxHandler } from "./keyTypeHandlers/astrax";
import { plaintextKeyHandler } from "./keyTypeHandlers/plaintextKey";
import { KeyType } from "./constants/keys";
/**
* The `KeyManager` class is your primary gateway API for encrypting and storing
* your users' DigitalBits keys. Make an instance of this and use that
* instance to create, read, update, and delete secret keys.
*
* Note that at this time, `KeyManager` does not generate keys, nor does it
* provide UI for accepting it from a user. You're app will have to implement
* those features and pass the resulting keys to this class.
*
* `KeyManager` employs a plugin system. You may implement three types of
* interfaces and add them to the `KeyManager` (or use our reference
* plugins):
*
* - A `Encrypter` handles encrypting and decrypting a key.
* - A `KeyStore` handles storing, updating, loading, and removing your keys
* after they've been encrypted.
* - (optional) A `KeyTypeHandler` encodes how to handle keytypes. For example,
* raw DigitalBits secret seeds may be signed differently than, say, how Ledger
* keys sign transactions (Ledger does not currently support DigitalBits
* blockchain accounts but it may in the future).
*
* Normally, you won't have to write `KeyTypeHandler` interfaces; the SDK
* currently provides handlers for these key types:
*
* - AstraX
* - Plaintext secrets
*
* ### Plugin names
*
* Each plugin you pass to `KeyManager` will have a `name` property, which
* should be unique to that particular interface and to the `KeyManager`. So if
* you make an `Encrypter` named "YourUniqueEncrypter", we'll save all your
* user's keys with that encrypter name, and we'll look for an `Encrypter` of
* that name to decrypt those keys until the end of time!
*/
var KeyManager = /** @class */ (function () {
function KeyManager(params) {
var _a;
this.encrypterMap = {};
this.keyHandlerMap = (_a = {},
_a[KeyType.astrax] = astraxHandler,
_a[KeyType.plaintextKey] = plaintextKeyHandler,
_a);
this.keyCache = {};
this.keyStore = params.keyStore;
this.shouldCache = params.shouldCache || false;
}
/**
* Register a KeyTypeHandler for a given key type.
* @param {KeyTypeHandler} keyHandler
*/
KeyManager.prototype.registerKeyHandler = function (keyHandler) {
this.keyHandlerMap[keyHandler.keyType] = keyHandler;
};
/**
* Register a new encrypter.
* @param {Encrypter} encrypter
*/
KeyManager.prototype.registerEncrypter = function (encrypter) {
this.encrypterMap[encrypter.name] = encrypter;
};
/**
* Stores a key in the keyStore after encrypting it with the encrypterName.
*
* @async
* @param key Key object to store. an `id` field is optional; if you don't
* provide one, we'll generate a random number. The id will be used to read,
* change, update, and delete keys.
* @param password encrypt key with this as the secret
* @param encrypterName encryption algorithm to use (must have been
* registered)
*
* @returns The metadata of the key
*/
KeyManager.prototype.storeKey = function (params) {
return __awaiter(this, void 0, void 0, function () {
var key, password, encrypterName, id, newKey, encrypter, encryptedKey, keyMetadata;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
key = params.key, password = params.password, encrypterName = params.encrypterName;
id = key.id || "".concat(Math.random());
newKey = __assign(__assign({}, key), { id: id });
encrypter = this.encrypterMap[encrypterName];
return [4 /*yield*/, (encrypter === null || encrypter === void 0 ? void 0 : encrypter.encryptKey({
key: newKey,
password: password,
}))];
case 1:
encryptedKey = _a.sent();
return [4 /*yield*/, this.keyStore.storeKeys([
encryptedKey,
])];
case 2:
keyMetadata = _a.sent();
this._writeIndexCache(newKey.id, newKey);
return [2 /*return*/, keyMetadata[0]];
}
});
});
};
/**
* Load and decrypt one key, given its id.
*
* @returns Decrypted key
*/
KeyManager.prototype.loadKey = function (id, password) {
return __awaiter(this, void 0, void 0, function () {
var encryptedKeys, keys, encryptedKey, encrypter, key, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.keyStore.loadAllKeys()];
case 1:
encryptedKeys = _a.sent();
keys = encryptedKeys.filter(function (k) { return k.id === id; });
if (!keys.length) {
throw new Error("Key not found with id '".concat(id, "'."));
}
if (keys.length > 1) {
throw new Error("Too many keys found with id '".concat(id, "', that's not supposed to happen!"));
}
encryptedKey = keys[0];
encrypter = this.encrypterMap[encryptedKey.encrypterName];
_a.label = 2;
case 2:
_a.trys.push([2, 4, , 5]);
return [4 /*yield*/, encrypter.decryptKey({
encryptedKey: encryptedKey,
password: password,
})];
case 3:
key = _a.sent();
return [3 /*break*/, 5];
case 4:
e_1 = _a.sent();
throw new Error("Couldn't decrypt key '".concat(id, "' with the supplied password."));
case 5: return [2 /*return*/, key];
}
});
});
};
/**
* Get a list of all stored key ids.
*
* @returns List of ids
*/
KeyManager.prototype.loadAllKeyIds = function () {
return __awaiter(this, void 0, void 0, function () {
var encryptedKeys;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.keyStore.loadAllKeys()];
case 1:
encryptedKeys = _a.sent();
return [2 /*return*/, encryptedKeys.map(function (key) { return key.id; })];
}
});
});
};
/**
* Remove the key specified by this key id.
*
* @async
* @param id Specifies which key to remove.
* The id is computed as `sha1(private key + public key)`.
* @returns Metadata of the removed key
*/
KeyManager.prototype.removeKey = function (id) {
return __awaiter(this, void 0, void 0, function () {
var res;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.keyStore.removeKey(id)];
case 1:
res = _a.sent();
this._writeIndexCache(id, undefined);
return [2 /*return*/, res];
}
});
});
};
/**
* Sign a transaction using the specified key id. Supports both using a
* cached key and going out to the keystore to read and decrypt
*
* @async
* @param {Transaction} transaction Transaction object to sign
* @param {string} id Key to sign with. The id is computed as
* `sha1(private key + public key)`.
* @returns Signed transaction
*/
KeyManager.prototype.signTransaction = function (params) {
return __awaiter(this, void 0, void 0, function () {
var transaction, id, password, custom, key, encryptedKey, encrypter, keyHandler, signedTransaction;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
transaction = params.transaction, id = params.id, password = params.password, custom = params.custom;
key = this._readFromCache(id);
if (!!key) return [3 /*break*/, 3];
return [4 /*yield*/, this.keyStore.loadKey(id)];
case 1:
encryptedKey = _a.sent();
if (!encryptedKey) {
throw new Error("Couldn't sign the transaction: no key with id '".concat(id, "' found."));
}
encrypter = this.encrypterMap[encryptedKey.encrypterName];
return [4 /*yield*/, encrypter.decryptKey({
encryptedKey: encryptedKey,
password: password,
})];
case 2:
key = _a.sent();
this._writeIndexCache(id, key);
_a.label = 3;
case 3:
keyHandler = this.keyHandlerMap[key.type];
return [4 /*yield*/, keyHandler.signTransaction({
transaction: transaction,
key: key,
custom: custom,
})];
case 4:
signedTransaction = _a.sent();
return [2 /*return*/, signedTransaction];
}
});
});
};
/**
* Update the stored keys to be encrypted with the new password.
*
* @async
* @param oldPassword the user's old password
* @param newPassword the user's new password
* @returns {Promise<KeyMetadata[]>}
*/
KeyManager.prototype.changePassword = function (params) {
return __awaiter(this, void 0, void 0, function () {
var oldPassword, newPassword, oldKeys, newKeys;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
oldPassword = params.oldPassword, newPassword = params.newPassword;
return [4 /*yield*/, this.keyStore.loadAllKeys()];
case 1:
oldKeys = _a.sent();
return [4 /*yield*/, Promise.all(oldKeys.map(function (encryptedKey) { return __awaiter(_this, void 0, void 0, function () {
var encrypter, decryptedKey;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
encrypter = this.encrypterMap[encryptedKey.encrypterName];
return [4 /*yield*/, encrypter.decryptKey({
encryptedKey: encryptedKey,
password: oldPassword,
})];
case 1:
decryptedKey = _a.sent();
this._writeIndexCache(decryptedKey.id, decryptedKey);
return [2 /*return*/, encrypter.encryptKey({
key: decryptedKey,
password: newPassword,
})];
}
});
}); }))];
case 2:
newKeys = _a.sent();
return [2 /*return*/, this.keyStore.updateKeys(newKeys)];
}
});
});
};
KeyManager.prototype._readFromCache = function (id) {
if (!this.shouldCache) {
return undefined;
}
return this.keyCache[id];
};
KeyManager.prototype._writeIndexCache = function (id, key) {
if (this.shouldCache && key) {
this.keyCache[id] = key;
}
};
return KeyManager;
}());
export { KeyManager };
//# sourceMappingURL=KeyManager.js.map