UNPKG

@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
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