@pokt-network/pocket-js
Version:
Pocket-js core package with the main functionalities to interact with the Pocket Network.
951 lines • 47.8 kB
JavaScript
"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