UNPKG

@pokt-network/pocket-js

Version:

Pocket-js core package with the main functionalities to interact with the Pocket Network.

951 lines 47.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Keybase = void 0; var account_1 = require("./models/account"); var libsodium_wrappers_1 = __importDefault(require("libsodium-wrappers")); var unlocked_account_1 = require("./models/unlocked-account"); var pbkdf2_1 = __importDefault(require("pbkdf2")); var js_sha256_1 = require("js-sha256"); var aes_js_1 = __importDefault(require("aes-js")); var type_guard_1 = require("../utils/type-guard"); var key_pair_1 = require("../utils/key-pair"); var __1 = require(".."); var cryptoLib = __importStar(require("crypto")); var scrypt_js_1 = __importDefault(require("scrypt-js")); /** * @author Luis C. de León <luis@pokt.network> * @description The Keybase class allows storage, operations and persistence of accounts. */ var Keybase = /** @class */ (function () { /** * @description Constructor for the Keybase class * @param {IKVStore} store - The IKVStore to use to store encrypted accounts * @memberof Keybase */ function Keybase(store) { this.ACCOUNT_STORE_PREFIX = "account_"; this.ACCOUNT_INDEX_KEY = "account_index"; this.store = store; this.unlockedAccounts = {}; } /** * Utility function to sign an arbitrary payload with a valid ed25519 private key * @param {Buffer} privateKey - The private key to sign with * @param {Buffer} payload - Arbitrary payload to sign * @returns {Buffer | Error} The signature or an Error * @memberof Keybase */ Keybase.signWith = function (privateKey, payload) { return __awaiter(this, void 0, void 0, function () { var err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); // We first wait for Sodium to be ready return [4 /*yield*/, libsodium_wrappers_1.default.ready]; case 1: // We first wait for Sodium to be ready _a.sent(); return [2 /*return*/, Buffer.from(libsodium_wrappers_1.default.crypto_sign_detached(payload, privateKey))]; case 2: err_1 = _a.sent(); return [2 /*return*/, err_1]; case 3: return [2 /*return*/]; } }); }); }; /** * @description Creates an account and stores it in the keybase * @param {string} passphrase The passphrase for the account in this keybase * @returns {Promise<Account | Error>} The the new account or an Error * @memberof Keybase */ Keybase.prototype.createAccount = function (passphrase) { return __awaiter(this, void 0, void 0, function () { var keypair, err_2; return __generator(this, function (_a) { switch (_a.label) { case 0: if (passphrase.length === 0) { return [2 /*return*/, new Error("Empty passphrase")]; } _a.label = 1; case 1: _a.trys.push([1, 4, , 5]); // Create the keypair return [4 /*yield*/, libsodium_wrappers_1.default.ready]; case 2: // Create the keypair _a.sent(); keypair = libsodium_wrappers_1.default.crypto_sign_keypair(); return [4 /*yield*/, this.importAccount(Buffer.from(keypair.privateKey), passphrase)]; case 3: return [2 /*return*/, _a.sent()]; case 4: err_2 = _a.sent(); return [2 /*return*/, err_2]; case 5: return [2 /*return*/]; } }); }); }; /** * @description Lists all the accounts stored in this keybase * @returns {Promise<Account | Error>} The the new account or an Error * @memberof Keybase */ Keybase.prototype.listAccounts = function () { return __awaiter(this, void 0, void 0, function () { var result, accountIndex, _a, _b, _i, index, accountOrError; return __generator(this, function (_c) { switch (_c.label) { case 0: result = new Array(); accountIndex = this.store.get(this.ACCOUNT_INDEX_KEY); if (type_guard_1.typeGuard(accountIndex, Array)) { accountIndex = accountIndex; } else if (accountIndex === undefined) { accountIndex = []; } else { return [2 /*return*/, new Error("Error fetching account index")]; } _a = []; for (_b in accountIndex) _a.push(_b); _i = 0; _c.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 4]; index = _a[_i]; return [4 /*yield*/, this.getAccount(accountIndex[index])]; case 2: accountOrError = _c.sent(); if (type_guard_1.typeGuard(accountOrError, account_1.Account)) { result.push(accountOrError); } else { return [2 /*return*/, accountOrError]; } _c.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, result]; } }); }); }; /** * @description Retrieves a single account from this keybase * @param {string} addressHex - The address of the account to retrieve in hex string format * @returns {Promise<Account | Error>} - The the new account or an Error * @memberof Keybase */ Keybase.prototype.getAccount = function (addressHex) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.getAccountFromStore(addressHex)]; }); }); }; /** * Generates a one time use UnlockedAccount from a persisted Account * @param {string} addressHex - The address hex for the account * @param {string} passphrase - The passphrase for the account * @returns {Promise<UnlockedAccount | Error>} The UnlockedAccount object or an Error * @memberof Keybase */ Keybase.prototype.getUnlockedAccount = function (addressHex, passphrase) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.unlockAccountFromPersistence(addressHex, passphrase)]; }); }); }; /** * @description Deletes an account stored in the keybase * @param {string} addressHex - The address of the account to delete in hex string format * @param {string} passphrase - The passphrase for the account in this keybase * @returns {Promise<Error | undefined>} undefined if the account was deleted or an Error * @memberof Keybase */ Keybase.prototype.deleteAccount = function (addressHex, passphrase) { return __awaiter(this, void 0, void 0, function () { var validationError, error, unlockedAccountOrError, unlockedAccount; return __generator(this, function (_a) { switch (_a.label) { case 0: validationError = key_pair_1.validateAddressHex(addressHex); if (validationError) { return [2 /*return*/, validationError]; } return [4 /*yield*/, this.unlockAccountFromPersistence(addressHex, passphrase)]; case 1: unlockedAccountOrError = _a.sent(); if (type_guard_1.typeGuard(unlockedAccountOrError, unlocked_account_1.UnlockedAccount)) { unlockedAccount = unlockedAccountOrError; return [2 /*return*/, this.removeAccountRecord(unlockedAccount)]; } else if (type_guard_1.typeGuard(unlockedAccountOrError, Error)) { error = unlockedAccountOrError; } else { error = new Error("Unknown error decrypting Account"); } return [2 /*return*/, error]; } }); }); }; /** * * @param {string} addressHex The address of the account to update in hex string format * @param {string} passphrase The passphrase of the account * @param {string} newPassphrase The new passphrase that the account will be updated to * @returns {Promise<Error | undefined>} undefined if the account passphrase was updated or an Error * @memberof Keybase */ Keybase.prototype.updateAccountPassphrase = function (addressHex, passphrase, newPassphrase) { return __awaiter(this, void 0, void 0, function () { var validationError, error, unlockedAccountOrError, unlockedAccount, errorOrUndefined, importedAccountOrError; return __generator(this, function (_a) { switch (_a.label) { case 0: validationError = key_pair_1.validateAddressHex(addressHex); if (validationError) { return [2 /*return*/, validationError]; } return [4 /*yield*/, this.unlockAccountFromPersistence(addressHex, passphrase)]; case 1: unlockedAccountOrError = _a.sent(); if (!type_guard_1.typeGuard(unlockedAccountOrError, unlocked_account_1.UnlockedAccount)) return [3 /*break*/, 3]; unlockedAccount = unlockedAccountOrError; return [4 /*yield*/, this.removeAccountRecord(unlockedAccount)]; case 2: errorOrUndefined = _a.sent(); if (type_guard_1.typeGuard(errorOrUndefined, Error)) { error = errorOrUndefined; } else { importedAccountOrError = this.importAccount(unlockedAccount.privateKey, newPassphrase); if (type_guard_1.typeGuard(importedAccountOrError, Error)) { error = importedAccountOrError; } } return [3 /*break*/, 4]; case 3: if (type_guard_1.typeGuard(unlockedAccountOrError, Error)) { error = unlockedAccountOrError; } else { error = new Error("Unknown error decrypting Account"); } _a.label = 4; case 4: return [2 /*return*/, error]; } }); }); }; // Keybase ECDSA functions /** * @param {string} addressHex The address of the account that will sign the payload in hex string format * @param {string} passphrase The passphrase of the account * @param {Buffer} payload The payload to be signed * @returns {Promise<Buffer | Error>} Signature buffer or an Error * @memberof Keybase */ Keybase.prototype.sign = function (addressHex, passphrase, payload) { return __awaiter(this, void 0, void 0, function () { var unlockedAccountOrError, unlockedAccount, err_3; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.unlockAccountFromPersistence(addressHex, passphrase)]; case 1: unlockedAccountOrError = _a.sent(); if (type_guard_1.typeGuard(unlockedAccountOrError, Error)) { return [2 /*return*/, unlockedAccountOrError]; } unlockedAccount = unlockedAccountOrError; _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, libsodium_wrappers_1.default.ready]; case 3: _a.sent(); return [2 /*return*/, Buffer.from(libsodium_wrappers_1.default.crypto_sign_detached(payload, unlockedAccount.privateKey))]; case 4: err_3 = _a.sent(); return [2 /*return*/, err_3]; case 5: return [2 /*return*/]; } }); }); }; /** * @description Signs a payload with an unlocked account stored in this keybase * @param {string} addressHex The address of the account that will sign the payload in hex string format * @param {Buffer} payload The payload to be signed * @returns {Promise<Buffer | Error>} Signature buffer or an Error * @memberof Keybase */ Keybase.prototype.signWithUnlockedAccount = function (addressHex, payload) { return __awaiter(this, void 0, void 0, function () { var validationError, isUnlocked, unlockedAccount, err_4; return __generator(this, function (_a) { switch (_a.label) { case 0: validationError = key_pair_1.validateAddressHex(addressHex); if (validationError) { return [2 /*return*/, validationError]; } return [4 /*yield*/, this.isUnlocked(addressHex)]; case 1: isUnlocked = _a.sent(); if (!isUnlocked) { return [2 /*return*/, new Error("Account is not unlocked")]; } _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); unlockedAccount = this.unlockedAccounts[addressHex]; return [4 /*yield*/, libsodium_wrappers_1.default.ready]; case 3: _a.sent(); return [2 /*return*/, Buffer.from(libsodium_wrappers_1.default.crypto_sign_detached(payload, unlockedAccount.privateKey))]; case 4: err_4 = _a.sent(); return [2 /*return*/, err_4]; case 5: return [2 /*return*/]; } }); }); }; /** * @description Verify the signature for a given payload signed by `signedPublicKey` * @param {Buffer} signerPublicKey The public key of the signer * @param {Buffer} payload The payload to be verified against the signature * @param {Buffer} signature The calculated signature for the payload * @returns {Promise<Buffer | Error>} Signature buffer or an Error * @memberof Keybase */ Keybase.prototype.verifySignature = function (signerPublicKey, payload, signature) { return __awaiter(this, void 0, void 0, function () { var err_5; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, libsodium_wrappers_1.default.ready]; case 1: _a.sent(); return [2 /*return*/, libsodium_wrappers_1.default.crypto_sign_verify_detached(signature, payload, signerPublicKey)]; case 2: err_5 = _a.sent(); return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); }; /** * @description Unlock an account for passphrase free signing of arbitrary payloads using `signWithUnlockedAccount` * @param {string} addressHex The address of the account that will be unlocked in hex string format * @param {string} passphrase The passphrase of the account to unlock * @param {number} unlockPeriod The amount of time (in ms) the account is going to be unlocked, defaults to 10 minutes. Use 0 to keep it unlocked indefinetely * @returns {Promise<Error | undefined>} Undefined if account got unlocked or an Error * @memberof Keybase */ Keybase.prototype.unlockAccount = function (addressHex, passphrase, unlockPeriod) { if (unlockPeriod === void 0) { unlockPeriod = 600000; } return __awaiter(this, void 0, void 0, function () { var unlockedAccountOrError, unlockedAccount; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.unlockAccountFromPersistence(addressHex, passphrase) // Return errors if any ]; case 1: unlockedAccountOrError = _a.sent(); // Return errors if any if (type_guard_1.typeGuard(unlockedAccountOrError, Error)) { return [2 /*return*/, unlockedAccountOrError]; } if (unlockPeriod > 0) { setTimeout(function (keybase, addressHex) { keybase.lockAccount(addressHex); }, unlockPeriod, this, addressHex); } unlockedAccount = unlockedAccountOrError; this.unlockedAccounts[unlockedAccount.addressHex] = unlockedAccount; return [2 /*return*/, undefined]; } }); }); }; /** * @description Locks an unlocked account back so signing payloads with it will require a passphrase * @param {string} addressHex The address of the account that will be locked in hex string format * @returns {Promise<Error | undefined>} Undefined if account got locked or an Error * @memberof Keybase */ Keybase.prototype.lockAccount = function (addressHex) { return __awaiter(this, void 0, void 0, function () { var validationError, isUnlocked; return __generator(this, function (_a) { switch (_a.label) { case 0: validationError = key_pair_1.validateAddressHex(addressHex); if (validationError) { return [2 /*return*/, validationError]; } return [4 /*yield*/, this.isUnlocked(addressHex)]; case 1: isUnlocked = _a.sent(); if (!isUnlocked) { return [2 /*return*/, new Error("Account is not unlocked")]; } delete this.unlockedAccounts[addressHex]; return [2 /*return*/, undefined]; } }); }); }; /** * @description Returns whether or not the specified account is unlocked * @param {string} addressHex The address of the account that will be verified in hex string format * @returns {Promise<boolean>} True or false if the account is unlocked * @memberof Keybase */ Keybase.prototype.isUnlocked = function (addressHex) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.unlockedAccounts[addressHex] ? true : false]; }); }); }; // Import/export of accounts /** * @description Imports an account by using it's private key into this keybase * @param {Buffer} privateKey The private key of the account to be imported into this keybase * @param {string} passphrase The passphrase of the account to be imported into the keybase * @returns {Promise<Account | Error>} Imported account or an Error * @memberof Keybase */ Keybase.prototype.importAccount = function (privateKey, passphrase) { return __awaiter(this, void 0, void 0, function () { var publicKey, key, encryptedPKHex, account, errorOrUndefined, err_6; return __generator(this, function (_a) { switch (_a.label) { case 0: if (passphrase.length === 0) { return [2 /*return*/, new Error("Empty passphrase")]; } if (!key_pair_1.validatePrivateKey(privateKey)) { return [2 /*return*/, new Error("Invalid private key")]; } _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); publicKey = key_pair_1.publicKeyFromPrivate(privateKey); key = pbkdf2_1.default.pbkdf2Sync(passphrase, js_sha256_1.sha256(passphrase), 1, 256 / 8); encryptedPKHex = aes_js_1.default.utils.hex.fromBytes(new aes_js_1.default.ModeOfOperation.ctr(key, undefined).encrypt(privateKey)); account = new account_1.Account(publicKey, encryptedPKHex); return [4 /*yield*/, this.persistAccount(account)]; case 2: errorOrUndefined = _a.sent(); if (type_guard_1.typeGuard(errorOrUndefined, Error)) { return [2 /*return*/, errorOrUndefined]; } else { return [2 /*return*/, account]; } return [3 /*break*/, 4]; case 3: err_6 = _a.sent(); return [2 /*return*/, err_6]; case 4: return [2 /*return*/]; } }); }); }; /** * @description Exports an account's private key stored in this keybase * @param {string} addressHex The address of the account that will be exported in hex string format * @param {string} passphrase The passphrase for the account that will be exported * @returns {Promise<Buffer | Error>} Exported account buffer or an Error * @memberof Keybase */ Keybase.prototype.exportAccount = function (addressHex, passphrase) { return __awaiter(this, void 0, void 0, function () { var unlockedAccountOrError, unlockedAccount; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.unlockAccountFromPersistence(addressHex, passphrase)]; case 1: unlockedAccountOrError = _a.sent(); if (type_guard_1.typeGuard(unlockedAccountOrError, Error)) { return [2 /*return*/, unlockedAccountOrError]; } unlockedAccount = unlockedAccountOrError; return [2 /*return*/, unlockedAccount.privateKey]; } }); }); }; /** * @description Creates a Portable Private Key(PPK) using an Account * @param {string} addressHex - Account's address hex. * @param {string} password - Desired password for the PPK. * @param {string} hint - (Optional) Private key hint. * @param {string} passphrase - Passphrase to unlock the account in the keybase. * @returns {Promise<string | Error>} * @memberof Keybase */ Keybase.prototype.exportPPKfromAccount = function (addressHex, password, hint, passphrase) { if (hint === void 0) { hint = ""; } return __awaiter(this, void 0, void 0, function () { var unlockedAccountOrError, ppkStrOrError; return __generator(this, function (_a) { switch (_a.label) { case 0: // Verify mandatory parameters if (password.length <= 0 || passphrase.length <= 0) { return [2 /*return*/, new Error("Wrong password or passphrase format, please try again with valid information.")]; } return [4 /*yield*/, this.getUnlockedAccount(addressHex, passphrase)]; case 1: unlockedAccountOrError = _a.sent(); if (!!type_guard_1.typeGuard(unlockedAccountOrError, Error)) return [3 /*break*/, 3]; return [4 /*yield*/, this.exportPPK(unlockedAccountOrError.privateKey.toString("hex"), password, hint) // Return the ppk or error ]; case 2: ppkStrOrError = _a.sent(); // Return the ppk or error return [2 /*return*/, ppkStrOrError]; case 3: return [2 /*return*/, unlockedAccountOrError]; } }); }); }; /** * @description Creates a Portable Private Key(PPK) by exporting to an armored JSON * @param {string} privateKey - Account raw private key. * @param {string} password - Desired password for the PPK. * @param {string} hint - (Optional) Private key hint. * @returns {Promise<string | Error>} * @memberof Keybase */ Keybase.prototype.exportPPK = function (privateKey, password, hint) { if (hint === void 0) { hint = ""; } return __awaiter(this, void 0, void 0, function () { var scryptHashLength, scryptOptions, secParam, algorithm, salt, scryptHash, scryptHashBuffer, iv, cipher, cipherText, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: // Check parameters if (privateKey.length <= 0 || password.length <= 0) { return [2 /*return*/, new Error("Invalid export PPK properties, please try again with valid information.")]; } scryptHashLength = 32; scryptOptions = { N: 32768, r: 8, p: 1, maxmem: 4294967290 }; secParam = 12; algorithm = "aes-256-gcm"; _a.label = 1; case 1: _a.trys.push([1, 5, , 6]); return [4 /*yield*/, cryptoLib.randomBytes(16)]; case 2: salt = _a.sent(); return [4 /*yield*/, scrypt_js_1.default.syncScrypt(Buffer.from(password, "utf8"), salt, scryptOptions.N, scryptOptions.r, scryptOptions.p, scryptHashLength) // Create the nonce from the first 12 bytes of the sha256 Scrypt hash ]; case 3: scryptHash = _a.sent(); scryptHashBuffer = Buffer.from(scryptHash); iv = Buffer.allocUnsafe(secParam); scryptHashBuffer.copy(iv, 0, 0, secParam); return [4 /*yield*/, cryptoLib.createCipheriv(algorithm, scryptHashBuffer, iv)]; case 4: cipher = _a.sent(); cipherText = cipher.update(privateKey, "utf8", "hex"); cipherText += cipher.final("hex"); // Concatenate the ciphertext final + auth tag cipherText = cipherText + cipher.getAuthTag().toString("hex"); // Returns the Armored JSON string return [2 /*return*/, JSON.stringify({ kdf: "scrypt", salt: salt.toString("hex"), secparam: secParam.toString(), hint: hint, ciphertext: Buffer.from(cipherText, "hex").toString("base64") })]; case 5: error_1 = _a.sent(); return [2 /*return*/, error_1]; case 6: return [2 /*return*/]; } }); }); }; /** * @description Imports a Portable Private Key(PPK) an armored JSON and stores in the keybase * @param {string} password - Desired password for the PPK. * @param {string} salt - Salt hex value. * @param {string} secParam - Security param. * @param {string} hint - (Optional) ppk hint * @param {string} cipherText - Generated ciphertext. * @param {string} passphrase - Passphrase to store in the keybase. * @returns {Promise<Account | Error>} * @memberof Keybase */ Keybase.prototype.importPPK = function (password, salt, secParam, hint, cipherText, passphrase) { if (secParam === void 0) { secParam = 12; } if (hint === void 0) { hint = ""; } return __awaiter(this, void 0, void 0, function () { var kdf, jsonStr, ppkOrError; return __generator(this, function (_a) { switch (_a.label) { case 0: kdf = "scrypt"; if (!(password.length === 0 || passphrase.length === 0)) return [3 /*break*/, 1]; return [2 /*return*/, new Error("The password or passphrase is invalid.")]; case 1: jsonStr = JSON.stringify({ kdf: kdf, salt: salt, secparam: secParam, hint: hint, ciphertext: cipherText }); // Validate the jsonStr if (!this.validatePPKJSON(jsonStr)) return [2 /*return*/, new Error("Failed to validate the ppk information, please try again with valid information.") // Create ppk ]; return [4 /*yield*/, this.importPPKFromJSON(password, jsonStr, passphrase) // Return PPK or Error ]; case 2: ppkOrError = _a.sent(); // Return PPK or Error return [2 /*return*/, ppkOrError]; } }); }); }; /** * @description Imports a Portable Private Key(PPK) an armored JSON and stores in the keybase * @param {string} password - Desired password for the PPK. * @param {string} jsonStr - Armored JSON with the PPK information. * @param {string} passphrase - Desired passphrase to store the account in the keybase. * @returns {Promise<Account | Error>} * @memberof Keybase */ Keybase.prototype.importPPKFromJSON = function (password, jsonStr, passphrase) { return __awaiter(this, void 0, void 0, function () { var jsonObject, scryptHashLength, ivLength, tagLength, algorithm, scryptOptions, decryptSalt, scryptHash, inputBuffer, iv, tag, data, decipher, result, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: if (password.length === 0 || !this.validatePPKJSON(jsonStr) || passphrase.length === 0) { return [2 /*return*/, new Error("One or more parameters are empty strings.")]; } _a.label = 1; case 1: _a.trys.push([1, 4, , 5]); jsonObject = JSON.parse(jsonStr); scryptHashLength = 32; ivLength = Number(jsonObject.secparam); tagLength = 16; algorithm = "aes-256-gcm"; scryptOptions = { N: 32768, r: 8, p: 1, maxmem: 4294967290 }; decryptSalt = Buffer.from(jsonObject.salt, "hex"); return [4 /*yield*/, scrypt_js_1.default.syncScrypt(Buffer.from(password, "utf8"), decryptSalt, scryptOptions.N, scryptOptions.r, scryptOptions.p, scryptHashLength) // Create a buffer from the ciphertext ]; case 2: scryptHash = _a.sent(); inputBuffer = Buffer.from(jsonObject.ciphertext, 'base64'); iv = scryptHash.slice(0, ivLength); tag = inputBuffer.slice(inputBuffer.length - tagLength); data = inputBuffer.slice(0, inputBuffer.length - tagLength); decipher = cryptoLib.createDecipheriv(algorithm, Buffer.from(scryptHash), iv); // Set the auth tag decipher.setAuthTag(tag); result = decipher.update(data, undefined, "utf8"); result += decipher.final("utf8"); return [4 /*yield*/, this.importAccount(Buffer.from(result, "hex"), passphrase)]; case 3: // Return the imported account or error return [2 /*return*/, _a.sent()]; case 4: error_2 = _a.sent(); return [2 /*return*/, error_2]; case 5: return [2 /*return*/]; } }); }); }; // Private interface /** * Returns an `UnlockedAccount` object corresponding to the `Account` object stored in this keybase * @param {string} addressHex The address of the account that will be used to create an `UnlockedAccount` in hex string format * @param {string} passphrase The passphrase for the account that will be used * @returns {Promise<UnlockedAccount | Error>} Unlocked account or an Error * @memberof Keybase */ Keybase.prototype.unlockAccountFromPersistence = function (addressHex, passphrase) { return __awaiter(this, void 0, void 0, function () { var validationError, error, accountOrError, account, key, encryptedPKBytes, decryptedPKHex, decryptedPublicKey, decryptedAddress, unlockedAccount; return __generator(this, function (_a) { switch (_a.label) { case 0: validationError = key_pair_1.validateAddressHex(addressHex); if (validationError) { return [2 /*return*/, validationError]; } return [4 /*yield*/, this.getAccount(addressHex)]; case 1: accountOrError = _a.sent(); if (type_guard_1.typeGuard(accountOrError, account_1.Account)) { account = accountOrError; try { key = pbkdf2_1.default.pbkdf2Sync(passphrase, js_sha256_1.sha256(passphrase), 1, 256 / 8); encryptedPKBytes = aes_js_1.default.utils.hex.toBytes(account.encryptedPrivateKeyHex); decryptedPKHex = aes_js_1.default.utils.hex.fromBytes(new aes_js_1.default.ModeOfOperation.ctr(key, undefined).decrypt(encryptedPKBytes)); decryptedPublicKey = decryptedPKHex.slice(decryptedPKHex.length / 2, decryptedPKHex.length); decryptedAddress = key_pair_1.addressFromPublickey(Buffer.from(decryptedPublicKey, "hex")); // Check addresses to make sure the decrypted account matches the intended account if (account.addressHex !== decryptedAddress.toString("hex")) { return [2 /*return*/, new Error("Wrong passphrase for account")]; } unlockedAccount = new unlocked_account_1.UnlockedAccount(account, Buffer.from(decryptedPKHex, "hex")); return [2 /*return*/, unlockedAccount]; } catch (err) { return [2 /*return*/, err]; } } else if (type_guard_1.typeGuard(accountOrError, Error)) { error = accountOrError; } else { error = new Error("Unknown error getting account"); } return [2 /*return*/, error]; } }); }); }; // Internal persistence interface /** * Persists an account in the storage of this keybase * @param {Account} account * @returns {Promise<Buffer | Error>} Signature buffer or an Error * @memberof Keybase */ Keybase.prototype.persistAccount = function (account) { return __awaiter(this, void 0, void 0, function () { var accountKey, accountIndex; return __generator(this, function (_a) { accountKey = this.generateAccountStoreKey(account); // Add the account to the store this.store.add(accountKey, account); accountIndex = this.store.get(this.ACCOUNT_INDEX_KEY); if (type_guard_1.typeGuard(accountIndex, Array)) { accountIndex = accountIndex; } else if (accountIndex === undefined) { accountIndex = []; } else { return [2 /*return*/, new Error("Error fetching the account index")]; } // Push the address hex into the index accountIndex.push(account.addressHex); // Persist the index to the store this.store.add(this.ACCOUNT_INDEX_KEY, accountIndex); // Return undefined for no errors return [2 /*return*/, undefined]; }); }); }; /** * Removes an account from the storage of this keybase * @param {Account} account * @returns {Promise<Error | undefined>} Undefined if the account was removed or an Error * @memberof Keybase */ Keybase.prototype.removeAccountRecord = function (account) { return __awaiter(this, void 0, void 0, function () { var accountKey, accountIndex, index; return __generator(this, function (_a) { accountKey = this.generateAccountStoreKey(account); // Remove the account from the store this.store.remove(accountKey); accountIndex = this.store.get(this.ACCOUNT_INDEX_KEY); if (type_guard_1.typeGuard(accountIndex, Array)) { accountIndex = accountIndex; index = accountIndex.indexOf(account.addressHex, 0); if (index > -1) { accountIndex.splice(index, 1); } // Persist the index to the store this.store.add(this.ACCOUNT_INDEX_KEY, accountIndex); } // Return undefined for no errors return [2 /*return*/, undefined]; }); }); }; /** * Gets a properly parsed Account object from the store * @param {string} addressHex The address of the account in hex format * @returns {Promise<Account | Error>} Account from store or an Error * @memberof Keybase */ Keybase.prototype.getAccountFromStore = function (addressHex) { return __awaiter(this, void 0, void 0, function () { var result, validationError, account; return __generator(this, function (_a) { validationError = key_pair_1.validateAddressHex(addressHex); if (validationError) { return [2 /*return*/, validationError]; } account = this.store.get(this.generateAccountStoreKeyFromAddressHex(addressHex)); // Verify the account fetched if any if (account === undefined) { result = new Error("Account not found"); } else if (type_guard_1.typeGuard(account, account_1.Account)) { result = account; } else { result = new Error("Error fetching account from store"); } return [2 /*return*/, result]; }); }); }; /** * Generates a key to be used in the IKVStore for this keybase instance * @param {Account} account The account for which the key wants to be generated for * @returns {string} Account store key value * @memberof Keybase */ Keybase.prototype.generateAccountStoreKey = function (account) { return this.ACCOUNT_STORE_PREFIX + account.addressHex; }; /** * Generates a key to be used in the IKVStore for this keybase instance from the address of * the account in hex format * @param {string} addressHex The account for which the key wants to be generated for * @returns {string} Generated Account store key. * @memberof Keybase */ Keybase.prototype.generateAccountStoreKeyFromAddressHex = function (addressHex) { return this.ACCOUNT_STORE_PREFIX + addressHex; }; /** * Validates the PPK json string properties * @param {string} jsonStr - JSON String holding the ppk information. * @returns {boolean} True or false. * @memberof Keybase */ Keybase.prototype.validatePPKJSON = function (jsonStr) { var jsonObject = JSON.parse(jsonStr); // Check if undefined if (jsonObject.kdf === undefined || jsonObject.salt === undefined || jsonObject.secparam === undefined || jsonObject.ciphertext === undefined) { return false; } // Validate the properties if (jsonObject.kdf !== "scrypt" || !__1.Hex.isHex(jsonObject.salt) || jsonObject.secparam <= 0 || jsonObject.ciphertext.length <= 0) { return false; } return true; }; return Keybase; }()); exports.Keybase = Keybase; //# sourceMappingURL=keybase.js.map