UNPKG

xdb-digitalbits-sdk

Version:

xdb-digitalbits-sdk is a library for working with the DigitalBits Frontier server.

230 lines 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Utils = void 0; var tslib_1 = require("tslib"); var clone_1 = tslib_1.__importDefault(require("lodash/clone")); var randombytes_1 = tslib_1.__importDefault(require("randombytes")); var xdb_digitalbits_base_1 = require("xdb-digitalbits-base"); var errors_1 = require("./errors"); var Utils; (function (Utils) { function buildChallengeTx(serverKeypair, clientAccountID, homeDomain, timeout, networkPassphrase, webAuthDomain) { if (timeout === void 0) { timeout = 300; } if (clientAccountID.startsWith("M")) { throw Error("Invalid clientAccountID: multiplexed accounts are not supported."); } var account = new xdb_digitalbits_base_1.Account(serverKeypair.publicKey(), "-1"); var now = Math.floor(Date.now() / 1000); var value = randombytes_1.default(48).toString("base64"); var transaction = new xdb_digitalbits_base_1.TransactionBuilder(account, { fee: xdb_digitalbits_base_1.BASE_FEE, networkPassphrase: networkPassphrase, timebounds: { minTime: now, maxTime: now + timeout, }, }) .addOperation(xdb_digitalbits_base_1.Operation.manageData({ name: homeDomain + " auth", value: value, source: clientAccountID, })) .addOperation(xdb_digitalbits_base_1.Operation.manageData({ name: "web_auth_domain", value: webAuthDomain, source: account.accountId(), })) .build(); transaction.sign(serverKeypair); return transaction .toEnvelope() .toXDR("base64") .toString(); } Utils.buildChallengeTx = buildChallengeTx; function readChallengeTx(challengeTx, serverAccountID, networkPassphrase, homeDomains, webAuthDomain) { var _a; if (serverAccountID.startsWith("M")) { throw Error("Invalid serverAccountID: multiplexed accounts are not supported."); } var transaction = xdb_digitalbits_base_1.TransactionBuilder.fromXDR(challengeTx, networkPassphrase); if (!(transaction instanceof xdb_digitalbits_base_1.Transaction)) { throw new errors_1.InvalidSep10ChallengeError("Invalid challenge: expected a Transaction but received a FeeBumpTransaction"); } var sequence = Number.parseInt(transaction.sequence, 10); if (sequence !== 0) { throw new errors_1.InvalidSep10ChallengeError("The transaction sequence number should be zero"); } if (transaction.source !== serverAccountID) { throw new errors_1.InvalidSep10ChallengeError("The transaction source account is not equal to the server's account"); } if (transaction.operations.length < 1) { throw new errors_1.InvalidSep10ChallengeError("The transaction should contain at least one operation"); } var _b = transaction.operations, operation = _b[0], subsequentOperations = _b.slice(1); if (!operation.source) { throw new errors_1.InvalidSep10ChallengeError("The transaction's operation should contain a source account"); } var clientAccountID = operation.source; if (operation.type !== "manageData") { throw new errors_1.InvalidSep10ChallengeError("The transaction's operation type should be 'manageData'"); } if (transaction.timeBounds && Number.parseInt((_a = transaction.timeBounds) === null || _a === void 0 ? void 0 : _a.maxTime, 10) === xdb_digitalbits_base_1.TimeoutInfinite) { throw new errors_1.InvalidSep10ChallengeError("The transaction requires non-infinite timebounds"); } if (!validateTimebounds(transaction)) { throw new errors_1.InvalidSep10ChallengeError("The transaction has expired"); } if (operation.value === undefined) { throw new errors_1.InvalidSep10ChallengeError("The transaction's operation values should not be null"); } if (!operation.value) { throw new errors_1.InvalidSep10ChallengeError("The transaction's operation value should not be null"); } if (Buffer.from(operation.value.toString(), "base64").length !== 48) { throw new errors_1.InvalidSep10ChallengeError("The transaction's operation value should be a 64 bytes base64 random string"); } if (!homeDomains) { throw new errors_1.InvalidSep10ChallengeError("Invalid homeDomains: a home domain must be provided for verification"); } var matchedHomeDomain; if (typeof homeDomains === "string") { if (homeDomains + " auth" === operation.name) { matchedHomeDomain = homeDomains; } } else if (Array.isArray(homeDomains)) { matchedHomeDomain = homeDomains.find(function (domain) { return domain + " auth" === operation.name; }); } else { throw new errors_1.InvalidSep10ChallengeError("Invalid homeDomains: homeDomains type is " + typeof homeDomains + " but should be a string or an array"); } if (!matchedHomeDomain) { throw new errors_1.InvalidSep10ChallengeError("Invalid homeDomains: the transaction's operation key name does not match the expected home domain"); } for (var _i = 0, subsequentOperations_1 = subsequentOperations; _i < subsequentOperations_1.length; _i++) { var op = subsequentOperations_1[_i]; if (op.type !== "manageData") { throw new errors_1.InvalidSep10ChallengeError("The transaction has operations that are not of type 'manageData'"); } if (op.source !== serverAccountID) { throw new errors_1.InvalidSep10ChallengeError("The transaction has operations that are unrecognized"); } if (op.name === "web_auth_domain") { if (op.value === undefined) { throw new errors_1.InvalidSep10ChallengeError("'web_auth_domain' operation value should not be null"); } if (op.value.compare(Buffer.from(webAuthDomain))) { throw new errors_1.InvalidSep10ChallengeError("'web_auth_domain' operation value does not match " + webAuthDomain); } } } return { tx: transaction, clientAccountID: clientAccountID, matchedHomeDomain: matchedHomeDomain }; } Utils.readChallengeTx = readChallengeTx; function verifyChallengeTxThreshold(challengeTx, serverAccountID, networkPassphrase, threshold, signerSummary, homeDomains, webAuthDomain) { var _a; var signers = signerSummary.map(function (signer) { return signer.key; }); var signersFound = verifyChallengeTxSigners(challengeTx, serverAccountID, networkPassphrase, signers, homeDomains, webAuthDomain); var weight = 0; var _loop_1 = function (signer) { var sigWeight = ((_a = signerSummary.find(function (s) { return s.key === signer; })) === null || _a === void 0 ? void 0 : _a.weight) || 0; weight += sigWeight; }; for (var _i = 0, signersFound_1 = signersFound; _i < signersFound_1.length; _i++) { var signer = signersFound_1[_i]; _loop_1(signer); } if (weight < threshold) { throw new errors_1.InvalidSep10ChallengeError("signers with weight " + weight + " do not meet threshold " + threshold + "\""); } return signersFound; } Utils.verifyChallengeTxThreshold = verifyChallengeTxThreshold; function verifyChallengeTxSigners(challengeTx, serverAccountID, networkPassphrase, signers, homeDomains, webAuthDomain) { var tx = readChallengeTx(challengeTx, serverAccountID, networkPassphrase, homeDomains, webAuthDomain).tx; var serverKP; try { serverKP = xdb_digitalbits_base_1.Keypair.fromPublicKey(serverAccountID); } catch (err) { throw new Error("Couldn't infer keypair from the provided 'serverAccountID': " + err.message); } var clientSigners = new Set(); for (var _i = 0, signers_1 = signers; _i < signers_1.length; _i++) { var signer = signers_1[_i]; if (signer === serverKP.publicKey()) { continue; } if (signer.charAt(0) !== "G") { continue; } clientSigners.add(signer); } if (clientSigners.size === 0) { throw new errors_1.InvalidSep10ChallengeError("No verifiable client signers provided, at least one G... address must be provided"); } var allSigners = tslib_1.__spreadArrays([ serverKP.publicKey() ], Array.from(clientSigners)); var signersFound = gatherTxSigners(tx, allSigners); if (signersFound.indexOf(serverKP.publicKey()) === -1) { throw new errors_1.InvalidSep10ChallengeError("Transaction not signed by server: '" + serverKP.publicKey() + "'"); } if (signersFound.length === 1) { throw new errors_1.InvalidSep10ChallengeError("None of the given signers match the transaction signatures"); } if (signersFound.length !== tx.signatures.length) { throw new errors_1.InvalidSep10ChallengeError("Transaction has unrecognized signatures"); } signersFound.splice(signersFound.indexOf(serverKP.publicKey()), 1); return signersFound; } Utils.verifyChallengeTxSigners = verifyChallengeTxSigners; function verifyTxSignedBy(transaction, accountID) { return gatherTxSigners(transaction, [accountID]).length !== 0; } Utils.verifyTxSignedBy = verifyTxSignedBy; function gatherTxSigners(transaction, signers) { var hashedSignatureBase = transaction.hash(); var txSignatures = clone_1.default(transaction.signatures); var signersFound = new Set(); for (var _i = 0, signers_2 = signers; _i < signers_2.length; _i++) { var signer = signers_2[_i]; if (txSignatures.length === 0) { break; } var keypair = void 0; try { keypair = xdb_digitalbits_base_1.Keypair.fromPublicKey(signer); } catch (err) { throw new errors_1.InvalidSep10ChallengeError("Signer is not a valid address: " + err.message); } for (var i = 0; i < txSignatures.length; i++) { var decSig = txSignatures[i]; if (!decSig.hint().equals(keypair.signatureHint())) { continue; } if (keypair.verify(hashedSignatureBase, decSig.signature())) { signersFound.add(signer); txSignatures.splice(i, 1); break; } } } return Array.from(signersFound); } Utils.gatherTxSigners = gatherTxSigners; function validateTimebounds(transaction) { if (!transaction.timeBounds) { return false; } var now = Math.floor(Date.now() / 1000); var _a = transaction.timeBounds, minTime = _a.minTime, maxTime = _a.maxTime; return (now >= Number.parseInt(minTime, 10) && now <= Number.parseInt(maxTime, 10)); } })(Utils = exports.Utils || (exports.Utils = {})); //# sourceMappingURL=utils.js.map