UNPKG

@radixdlt/rola

Version:

Radix TypeScript ROLA library

247 lines (235 loc) 9.93 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/rola.ts var rola_exports = {}; __export(rola_exports, { Rola: () => Rola }); module.exports = __toCommonJS(rola_exports); var import_neverthrow5 = require("neverthrow"); // src/helpers/create-signature-message.ts var import_buffer2 = require("buffer"); // src/crypto/blake2b.ts var import_neverthrow = require("neverthrow"); var import_blakejs = __toESM(require("blakejs"), 1); var import_buffer = require("buffer"); var toArrayBuffer = (buffer) => { const arrayBuffer = new ArrayBuffer(buffer.length); const view = new Uint8Array(arrayBuffer); for (let i = 0; i < buffer.length; ++i) { view[i] = buffer[i]; } return arrayBuffer; }; var bufferToUnit8Array = (buffer) => new Uint8Array(toArrayBuffer(buffer)); var blake2b = (input) => { try { return (0, import_neverthrow.ok)(import_blakejs.default.blake2bHex(bufferToUnit8Array(input), void 0, 32)).map( (hex) => import_buffer.Buffer.from(hex, "hex") ); } catch (error) { return (0, import_neverthrow.err)(error); } }; // src/helpers/create-signature-message.ts var createSignatureMessage = ({ challenge, dAppDefinitionAddress, origin }) => { const prefix = import_buffer2.Buffer.from("R", "ascii"); const lengthOfDappDefAddress = dAppDefinitionAddress.length; const lengthOfDappDefAddressBuffer = import_buffer2.Buffer.from( lengthOfDappDefAddress.toString(16), "hex" ); const dappDefAddressBuffer = import_buffer2.Buffer.from(dAppDefinitionAddress, "utf-8"); const originBuffer = import_buffer2.Buffer.from(origin, "utf-8"); const challengeBuffer = import_buffer2.Buffer.from(challenge, "hex"); const messageBuffer = import_buffer2.Buffer.concat([ prefix, challengeBuffer, lengthOfDappDefAddressBuffer, dappDefAddressBuffer, originBuffer ]); return blake2b(messageBuffer).map((hash) => import_buffer2.Buffer.from(hash).toString("hex")).mapErr((jsError) => ({ reason: "couldNotHashMessage", jsError })); }; // src/helpers/verify-proof.ts var import_neverthrow2 = require("neverthrow"); var import_secp256k1 = require("@noble/curves/secp256k1"); var import_ed25519 = require("@noble/curves/ed25519"); var supportedCurves = /* @__PURE__ */ new Set(["curve25519", "secp256k1"]); var verifyProofFactory = (input) => (signatureMessageHex) => { const isSupportedCurve = supportedCurves.has(input.proof.curve); if (!isSupportedCurve) return (0, import_neverthrow2.err)({ reason: "unsupportedCurve" }); try { let isValid = false; if (input.proof.curve === "curve25519") { isValid = import_ed25519.ed25519.verify( input.proof.signature, signatureMessageHex, input.proof.publicKey ); } else { const signature = input.proof.signature.slice(2); isValid = import_secp256k1.secp256k1.verify( signature, signatureMessageHex, input.proof.publicKey ); } return isValid ? (0, import_neverthrow2.ok)(void 0) : (0, import_neverthrow2.err)({ reason: "invalidSignature" }); } catch (error) { return (0, import_neverthrow2.err)({ reason: "invalidPublicKey", jsError: error }); } }; // src/helpers/derive-address-from-public-key.ts var import_radix_engine_toolkit = require("@radixdlt/radix-engine-toolkit"); var import_neverthrow3 = require("neverthrow"); // src/helpers/typed-error.ts var typedError = (error) => error; // src/helpers/derive-address-from-public-key.ts var deriveVirtualIdentityAddress = (publicKey, networkId) => import_neverthrow3.ResultAsync.fromPromise( import_radix_engine_toolkit.RadixEngineToolkit.Derive.virtualIdentityAddressFromPublicKey( new import_radix_engine_toolkit.PublicKey.Ed25519(publicKey), networkId ), typedError ); var deriveVirtualEddsaEd25519AccountAddress = (publicKey, networkId) => import_neverthrow3.ResultAsync.fromPromise( import_radix_engine_toolkit.RadixEngineToolkit.Derive.virtualAccountAddressFromPublicKey( new import_radix_engine_toolkit.PublicKey.Ed25519(publicKey), networkId ), typedError ); var deriveVirtualEcdsaSecp256k1AccountAddress = (publicKey, networkId) => import_neverthrow3.ResultAsync.fromPromise( import_radix_engine_toolkit.RadixEngineToolkit.Derive.virtualAccountAddressFromPublicKey( new import_radix_engine_toolkit.PublicKey.Secp256k1(publicKey), networkId ), typedError ); var deriveVirtualAddress = (signedChallenge, networkId) => { if (signedChallenge.type === "persona") return deriveVirtualIdentityAddress( signedChallenge.proof.publicKey, networkId ); else if (signedChallenge.type === "account" && signedChallenge.proof.curve === "curve25519") return deriveVirtualEddsaEd25519AccountAddress( signedChallenge.proof.publicKey, networkId ); else if (signedChallenge.type === "account" && signedChallenge.proof.curve === "secp256k1") return deriveVirtualEcdsaSecp256k1AccountAddress( signedChallenge.proof.publicKey, networkId ); return (0, import_neverthrow3.errAsync)(new Error("Could not derive virtual address")); }; // src/helpers/create-public-key-hash.ts var import_buffer3 = require("buffer"); var createPublicKeyHash = (publicKey) => blake2b(import_buffer3.Buffer.from(publicKey, "hex")).map((hash) => hash.subarray(-29)).map((hash) => import_buffer3.Buffer.from(hash).toString("hex")); // src/gateway.ts var import_babylon_gateway_api_sdk = require("@radixdlt/babylon-gateway-api-sdk"); var import_neverthrow4 = require("neverthrow"); var GatewayService = (input) => { var _a; const { networkId, applicationName } = input; const config = import_babylon_gateway_api_sdk.RadixNetworkConfigById[networkId]; if (!config) throw new Error(`Network ${networkId} not found`); const { state } = (_a = input.gatewayApiClient) != null ? _a : import_babylon_gateway_api_sdk.GatewayApiClient.initialize({ basePath: config.gatewayUrl, applicationName }); const getEntityDetails = (address) => import_neverthrow4.ResultAsync.fromPromise( state.getEntityDetailsVaultAggregated(address), typedError ); return { getEntityOwnerKeys: (address) => getEntityDetails(address).map( (response) => { var _a2, _b, _c; return (_c = (_b = (_a2 = response == null ? void 0 : response.metadata) == null ? void 0 : _a2.items.find((item) => item.key === "owner_keys")) == null ? void 0 : _b.value.raw_hex) != null ? _c : ""; } ) }; }; // src/rola.ts if (!globalThis.self) globalThis.self = globalThis; var Rola = (input) => { const { expectedOrigin, dAppDefinitionAddress, networkId, applicationName } = input; const gatewayService = GatewayService({ networkId, applicationName, gatewayApiClient: input.gatewayApiClient }); const verifySignedChallenge = (signedChallenge) => { const result = createPublicKeyHash(signedChallenge.proof.publicKey); if (result.isErr()) return (0, import_neverthrow5.errAsync)({ reason: "couldNotHashPublicKey" }); const hashedPublicKey = result.value; const verifyProof = verifyProofFactory(signedChallenge); const getDerivedAddress = () => deriveVirtualAddress(signedChallenge, networkId).mapErr((jsError) => ({ reason: "couldNotDeriveAddressFromPublicKey", jsError })); const queryLedger = () => gatewayService.getEntityOwnerKeys(signedChallenge.address).mapErr((jsError) => ({ reason: "couldNotVerifyPublicKeyOnLedger", jsError })).map((ownerKeys) => ({ ownerKeysMatchesProvidedPublicKey: ownerKeys.toUpperCase().includes(hashedPublicKey.toUpperCase()), ownerKeysSet: !!ownerKeys })); const deriveAddressFromPublicKeyAndQueryLedger = () => import_neverthrow5.ResultAsync.combine([getDerivedAddress(), queryLedger()]); return createSignatureMessage({ dAppDefinitionAddress, origin: expectedOrigin, challenge: signedChallenge.challenge }).andThen(verifyProof).asyncAndThen(deriveAddressFromPublicKeyAndQueryLedger).andThen( ([ derivedAddress, { ownerKeysMatchesProvidedPublicKey, ownerKeysSet } ]) => { const derivedAddressMatchesPublicKey = !ownerKeysSet && derivedAddress === signedChallenge.address; return ownerKeysMatchesProvidedPublicKey || derivedAddressMatchesPublicKey ? (0, import_neverthrow5.ok)(void 0) : (0, import_neverthrow5.err)({ reason: "invalidPublicKey" }); } ); }; return { verifySignedChallenge }; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Rola });