diff --git a/.vscode/launch.json b/.vscode/launch.json index 3b9fd80c..d1b97cde 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,7 +22,7 @@ "jsdom-global/register", "--allow-uncaught", "--colors", - "--timeout 50000", + "--timeout 100000", // "--reporter", // "mocha-reporter", "${workspaceFolder}/src/test/*.ts" @@ -35,7 +35,7 @@ "request": "attach", "port": 9229, "protocol": "inspector", - "timeout": 30000, + "timeout": 100000, "stopOnEntry": false } ] diff --git a/.vscode/settings.json b/.vscode/settings.json index ff99f0ed..613cb16f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ }, "mochaExplorer.files": ["src/test/*.ts", "src/test/crux-messenger/*.ts", "src/test/integration-tests/crux-messenger/*.ts"], "mochaExplorer.require": ["ts-node/register", "mock-local-storage", "jsdom-global/register"], - "mochaExplorer.timeout": 50000, + "mochaExplorer.timeout": 100000, "testExplorer.codeLens": true, "testExplorer.gutterDecoration": true, "testExplorer.onStart": "reset", diff --git a/package.json b/package.json index 7b157e9b..6f310b29 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,14 @@ "test": "TS_NODE_PROJECT='./src/test/tsconfig.commonjs.json' TS_NODE_TRANSPILE_ONLY=true ./node_modules/.bin/mocha --exit --require ts-node/register --require mock-local-storage --require jsdom-global/register --allow-uncaught --colors --reporter mocha-reporter --timeout 5000 src/test/*.ts src/test/crux-messenger/*.ts", "start-bridge-server": "node dist/crux-gateway-bridge-without-auth.js &", "stop-bridge-server": "pkill -- signal SIGINT crux-gateway-bridge-server-without-auth", - "integration-test": "TS_NODE_PROJECT='./src/test/tsconfig.commonjs.json' TS_NODE_TRANSPILE_ONLY=true ./node_modules/.bin/mocha --exit --require ts-node/register --require mock-local-storage --require jsdom-global/register --allow-uncaught --colors --reporter mocha-reporter --timeout 50000 src/test/integration-tests/crux-messenger/*.ts", + "integration-test": "TS_NODE_PROJECT='./src/test/tsconfig.commonjs.json' TS_NODE_TRANSPILE_ONLY=true ./node_modules/.bin/mocha --exit --require ts-node/register --require mock-local-storage --require jsdom-global/register --allow-uncaught --colors --reporter mocha-reporter --timeout 80000 src/test/integration-tests/crux-messenger/*.ts", "integration": "npm run build-crux-bridge-server-without-auth && npm run start-bridge-server && npm run integration-test && npm run stop-bridge-server", "copy-latest-docs": "cp -a docs/$npm_package_version/. docs/", "version-docs": "./node_modules/.bin/typedoc --out docs/$npm_package_version src/index.ts", "coverage": "./node_modules/.bin/nyc npm run test", "wallet_demo_legacy": "./node_modules/.bin/parcel src/samples/wallet_demo_legacy.html --https --no-cache", - "wallet_demo": "./node_modules/.bin/parcel src/samples/wallet_demo.html --no-cache", + "wallet_demo": "./node_modules/.bin/parcel src/samples/wallet_demo.html --no-cache &", + "remote-crux-settings": "npm run wallet_demo && ./node_modules/.bin/parcel src/samples/remote-crux-settings.html --no-cache", "onboarding_demo": "./node_modules/.bin/parcel src/samples/onboarding_demo.html --https --no-cache", "typecheck": "./node_modules/.bin/tsc --noEmit", "validate_lockfile": "./node_modules/.bin/lockfile-lint --type npm --path package-lock.json --allowed-hosts npm github.com --allowed-schemes \"https:\" \"git+https:\"", diff --git a/src/application/clients/crux-gateway-bridge-config.json b/src/application/clients/crux-gateway-bridge-config.json index d4911c3c..831c9617 100644 --- a/src/application/clients/crux-gateway-bridge-config.json +++ b/src/application/clients/crux-gateway-bridge-config.json @@ -3,7 +3,7 @@ "BROKER_HOST": "127.0.0.1" }, "PORTS": { - "BROKER_PORT": 1883, + "BROKER_PORT": 8000, "TCP_PORT": 4005 } } diff --git a/src/application/clients/crux-service-client.ts b/src/application/clients/crux-service-client.ts new file mode 100644 index 00000000..e35b2b6e --- /dev/null +++ b/src/application/clients/crux-service-client.ts @@ -0,0 +1,28 @@ +import { CruxProtocolMessenger, RemoteKeyManager} from "../../core/domain-services"; +import { keyManagementProtocol } from "../../infrastructure"; +import { CruxId, InMemStorage } from "../../packages/"; +import { CruxWalletClient } from "./crux-wallet-client"; + +export class CruxServiceClient { + private selfIdClaim: any; + private cruxProtocolMessenger: any; + + constructor(selfIdClaim: any, secureCruxNetwork: any, protocolSchema: any) { + this.selfIdClaim = selfIdClaim; + this.cruxProtocolMessenger = new CruxProtocolMessenger(secureCruxNetwork, protocolSchema); + } + + public async getWalletClientForUser(remoteUserId: CruxId) { + await this.cruxProtocolMessenger.initialize(); + const remoteKeyManager = new RemoteKeyManager(this.cruxProtocolMessenger, remoteUserId); + + await remoteKeyManager.initialize(); + return new CruxWalletClient({ + cacheStorage: new InMemStorage(), + disableCruxMessenger: true, + // @ts-ignore + privateKey: remoteKeyManager, + walletClientName: remoteUserId.components.domain, + }); + } +} diff --git a/src/application/clients/crux-wallet-client.ts b/src/application/clients/crux-wallet-client.ts index 51fc814b..a74f6d87 100644 --- a/src/application/clients/crux-wallet-client.ts +++ b/src/application/clients/crux-wallet-client.ts @@ -1,6 +1,6 @@ // Importing packages import Logger from "js-logger"; -import {CruxProtocolMessenger, SecureCruxNetwork} from "../../core/domain-services"; +import {CruxProtocolMessenger, RemoteKeyHost, SecureCruxNetwork} from "../../core/domain-services"; import { CruxDomain, CruxSpec, @@ -26,6 +26,7 @@ import { CruxNetPubSubClientFactory, cruxPaymentProtocol, IBlockstackCruxDomainRepositoryOptions, IBlockstackCruxUserRepositoryOptions, + keyManagementProtocol, } from "../../infrastructure/implementations"; import {CruxDomainId, CruxId, getLogger, InMemStorage, StorageService} from "../../packages"; import {Encryption} from "../../packages/encryption"; @@ -79,6 +80,7 @@ export interface ICruxWalletClientOptions { cacheStorage?: StorageService; walletClientName: string; debugLogging?: boolean; + disableCruxMessenger?: boolean; } export interface ICruxIDState { @@ -96,7 +98,7 @@ export const getCruxUserRepository = (options: IBlockstackCruxUserRepositoryOpti export const getPubsubClientFactory = (): IPubSubClientFactory => { return new CruxNetPubSubClientFactory({defaultLinkServer: { - host: "broker.hivemq.com", + host: "127.0.0.1", path: "/mqtt", port: 8000, }}); @@ -107,6 +109,7 @@ export class CruxWalletClient { public walletClientName: string; // TODO: make private public paymentProtocolMessenger?: CruxProtocolMessenger; + public keyManagerProtocolMessenger?: CruxProtocolMessenger; public secureCruxNetwork?: SecureCruxNetwork; private cruxBlockstackInfrastructure: ICruxBlockstackInfrastructure; private initPromise: Promise; @@ -119,6 +122,7 @@ export class CruxWalletClient { private resolvedClientAssetMapping?: IResolvedClientAssetMap; private cacheStorage?: StorageService; private selfCruxUser?: CruxUser; + private remoteKeyHost?: RemoteKeyHost; constructor(options: ICruxWalletClientOptions) { getLogger(cruxWalletClientDebugLoggerName).setLevel(options.debugLogging ? Logger.DEBUG : Logger.OFF); @@ -136,7 +140,7 @@ export class CruxWalletClient { } this.cruxDomainRepo = getCruxDomainRepository({cacheStorage: this.cacheStorage, blockstackInfrastructure: this.cruxBlockstackInfrastructure}); this.cruxDomainId = new CruxDomainId(this.walletClientName); - this.initPromise = this.asyncInit(); + this.initPromise = this.asyncInit(options); } @throwCruxClientError @@ -423,7 +427,7 @@ export class CruxWalletClient { return this.cruxDomain; } - private asyncInit = async (): Promise => { + private asyncInit = async (options: ICruxWalletClientOptions): Promise => { this.cruxDomain = await this.cruxDomainRepo.get(this.cruxDomainId); if (!this.cruxDomain) { throw ErrorHelper.getPackageError(null, PackageErrorCode.InvalidWalletClientName); @@ -433,9 +437,16 @@ export class CruxWalletClient { throw ErrorHelper.getPackageError(null, PackageErrorCode.CouldNotFindBlockstackConfigurationServiceClientConfig); } this.cruxAssetTranslator = new CruxAssetTranslator(this.cruxDomain.config.assetMapping, this.cruxDomain.config.assetList); + if (options.disableCruxMessenger) { + return; + } const selfIdClaim = await this.getSelfClaim(); if (selfIdClaim) { - await this.setupCruxMessenger(selfIdClaim); + try { + await this.setupCruxMessenger(selfIdClaim); + } catch (err) { + console.log(err); + } } } @@ -461,8 +472,12 @@ export class CruxWalletClient { } const pubsubClientFactory = getPubsubClientFactory(); this.secureCruxNetwork = new SecureCruxNetwork(this.cruxUserRepository, pubsubClientFactory, selfIdClaim); - await this.secureCruxNetwork.initialize(); this.paymentProtocolMessenger = new CruxProtocolMessenger(this.secureCruxNetwork, cruxPaymentProtocol); + this.keyManagerProtocolMessenger = new CruxProtocolMessenger(this.secureCruxNetwork, keyManagementProtocol); + await this.keyManagerProtocolMessenger.initialize(); + const remoteKeyHost = new RemoteKeyHost(this.keyManagerProtocolMessenger, this.keyManager!); + this.remoteKeyHost = remoteKeyHost; + await this.remoteKeyHost.initialize(); } } diff --git a/src/application/clients/index.ts b/src/application/clients/index.ts index a1346961..d16e255a 100644 --- a/src/application/clients/index.ts +++ b/src/application/clients/index.ts @@ -1,3 +1,4 @@ export * from "./crux-explorer-client"; export * from "./crux-wallet-client"; export * from "./crux-wallet-onboarding"; +export * from "./crux-service-client"; diff --git a/src/core/domain-services/crux-messenger.ts b/src/core/domain-services/crux-messenger.ts index 3696074c..794be387 100644 --- a/src/core/domain-services/crux-messenger.ts +++ b/src/core/domain-services/crux-messenger.ts @@ -19,6 +19,7 @@ import { export class CertificateManager { public static make = async (idClaim: ICruxIdClaim): Promise => { const payload = idClaim.cruxId.toString(); + console.log("CertificateManager::make:claim: ", payload); const signedProof = await idClaim.keyManager.signWebToken(payload); return { claim: idClaim.cruxId.toString(), @@ -28,6 +29,7 @@ export class CertificateManager { public static verify = (certificate: ICruxIdCertificate, senderPubKey: any) => { const proof: any = decodeToken(certificate.proof).payload; const verified = new TokenVerifier("ES256K", senderPubKey).verify(certificate.proof); + console.log("CertificateManager::verify:: verified, claim, proof", verified, certificate.claim, proof); if (proof && proof === certificate.claim && verified) { return true; } @@ -45,6 +47,7 @@ export class EncryptionManager { } public static decrypt = async (encryptedContent: string, keyManager: IKeyManager): Promise => { try { + console.log("EncryptionManager::decrypt::keymanager, encryptionContent: ", keyManager, encryptedContent); const decryptedContent = await keyManager.decryptMessage!(encryptedContent); return decryptedContent; } catch (e) { @@ -249,7 +252,6 @@ export class SecureContext { data, }; const serializedSecurePacket = JSON.stringify(securePacket); - const recipientCruxUser: CruxUser | undefined = await this.cruxUserRepo.getByCruxId(recipientId); if (!recipientCruxUser) { throw Error("No Such CRUX User Found"); diff --git a/src/core/domain-services/index.ts b/src/core/domain-services/index.ts index ed78b501..c9646e43 100644 --- a/src/core/domain-services/index.ts +++ b/src/core/domain-services/index.ts @@ -1 +1,2 @@ export * from "./crux-messenger"; +export * from "./remote-key-service"; diff --git a/src/core/domain-services/remote-key-service.ts b/src/core/domain-services/remote-key-service.ts new file mode 100644 index 00000000..a0f891b1 --- /dev/null +++ b/src/core/domain-services/remote-key-service.ts @@ -0,0 +1,153 @@ +import {makeUUID4} from "blockstack/lib"; +import { createNanoEvents, DefaultEvents, Emitter } from "nanoevents"; +import { CruxId } from "../../packages"; +import { IKeyManager } from "../interfaces"; +import { CruxProtocolMessenger, SecureCruxNetwork } from "./crux-messenger"; + +const VALID_METHODS = ["signWebToken", "getPubKey", "deriveSharedSecret", "decryptMessage"]; + +export class RemoteKeyClient { + private cruxProtocolMessenger: CruxProtocolMessenger; + private remoteUserId: CruxId; + private emitter: Emitter; + + constructor(cruxProtocolMessenger: CruxProtocolMessenger, remoteUserId: CruxId) { + this.cruxProtocolMessenger = cruxProtocolMessenger; + this.remoteUserId = remoteUserId; + this.emitter = createNanoEvents(); + } + + public async initialize() { + this.cruxProtocolMessenger.on("KEY_MANAGER_RESPONSE", async (msg: any, senderId: CruxId | undefined) => { + console.log("Inside RemoteKeyClient::initialize::Msg, senderId: ", msg, senderId); + this.emitter.emit(msg.invocationId, msg, senderId); + }); + } + + public async invoke(method: string, args: any[]) { + console.log("RemoteKeyClient::Inside Invoke"); + if (!this.cruxProtocolMessenger) { + throw Error("RemoteKeyClient cannot send with no secureCruxNetwork"); + } + const methodData = this.generateMethodData(method, args); + console.log("RemoteKeyClient::Inside Invoke, RemoteUserId, MethodData", this.remoteUserId, methodData); + // @ts-ignore + await this.cruxProtocolMessenger.send({ + content: methodData, + type: "KEY_MANAGER_REQUEST", + }, this.remoteUserId); + return methodData.invocationId; + } + + public listenToInvocation = (invocationId: string, resultCallback: (msg: any, senderId: CruxId | undefined) => any, errorCallback: (err: any) => any): void => { + if (!this.cruxProtocolMessenger) { + throw Error("RemoteKeyClient cannot listen with no secureCruxNetwork"); + } + console.log("RemoteKeyClient::ListenToInvocation::invocationId", invocationId); + this.emitter.on(invocationId, resultCallback); + this.emitter.on("error", errorCallback); + } + + private generateMethodData(method: string, args: any[]) { + return { + args, + invocationId: makeUUID4(), + method, + }; + } +} + +export class RemoteKeyHost { + private cruxProtocolMessenger: CruxProtocolMessenger; + private keyManager: IKeyManager; + + constructor(cruxProtocolMessenger: CruxProtocolMessenger, keyManager: IKeyManager) { + this.keyManager = keyManager; + this.cruxProtocolMessenger = cruxProtocolMessenger; + } + + public async initialize() { + this.cruxProtocolMessenger.on("KEY_MANAGER_REQUEST", async (msg: any, senderId: CruxId | undefined) => { + console.log("Inside RemoteKeyHost::in::Msg, senderId: ", msg, senderId); + const data = await this.handleMessage(msg); + console.log("Inside RemoteKeyHost::initialize::Data(handleMessage): ", data); + // @ts-ignore + await this.sendInvocationResult(data, CruxId.fromString(senderId!)); // getting senderId as string + }); + } + private async sendInvocationResult(result: any, receiverId: CruxId) { + if (!this.cruxProtocolMessenger) { + throw Error("RemoteKeyClient cannot send with no selfMessenger"); + } + const resultData = this.generateInvocationResponse(result); + console.log("RemoteKeyHost::Inside sendInvocationResult::resultData: ", resultData); + await this.cruxProtocolMessenger.send({ + content: resultData, + type: "KEY_MANAGER_RESPONSE", + }, receiverId); + } + + private async handleMessage(message: any) { + if (!this.keyManager) { + throw new Error("Key Manager not available"); + } + let data; + // @ts-ignore + data = await this.keyManager[message.method](message.args[0]); + return { + data, + invocationId: message.invocationId, + }; + } + + private generateInvocationResponse(result: any) { + return { + invocationId: result.invocationId, + result, + }; + } +} + +export class RemoteKeyManager implements IKeyManager { + private remoteKeyClient: RemoteKeyClient; + private remoteUserId: CruxId; + + constructor(cruxProtocolMessenger: CruxProtocolMessenger, remoteUserId: CruxId) { + this.remoteKeyClient = new RemoteKeyClient(cruxProtocolMessenger, remoteUserId); + this.remoteUserId = remoteUserId; + } + + public async initialize() { + await this.remoteKeyClient.initialize(); + } + // @ts-ignore + public signWebToken = async (args: any) => { + return this.makeRemoteMessageCall("signWebToken", [args]); + } + // @ts-ignore + public getPubKey = async () => { + return this.makeRemoteMessageCall("getPubKey"); + } + // @ts-ignore + public deriveSharedSecret = async (args: string) => { + return this.makeRemoteMessageCall("deriveSharedSecret", [args]); + } + // @ts-ignore + public decryptMessage = async (args: string) => { + return this.makeRemoteMessageCall("decryptMessage", [args]); + } + + private makeRemoteMessageCall = async (method: string, args: any = []) => { + console.log("makeRemoteMessageCall::", method, args); + const invocationId = await this.remoteKeyClient.invoke(method, args); + console.log("RemoteKeyManager::makeRemoteMessageCall::invokationId: ", invocationId); + return new Promise(async (resolve, reject) => { + this.remoteKeyClient.listenToInvocation(invocationId, (msg, senderId) => { + console.log("RemoteKeyManager::deriveSharedSecret::msg: ", msg); + resolve(msg.result.data); + }, (err) => { + reject(err); + }); + }); + } +} diff --git a/src/core/index.ts b/src/core/index.ts index b2dd8083..8fa0d752 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,2 +1,3 @@ export * from "./entities"; export * from "./interfaces"; +export * from "./domain-services"; diff --git a/src/infrastructure/implementations/basic-key-manager.ts b/src/infrastructure/implementations/basic-key-manager.ts index 27901749..75401dba 100644 --- a/src/infrastructure/implementations/basic-key-manager.ts +++ b/src/infrastructure/implementations/basic-key-manager.ts @@ -32,12 +32,14 @@ export class BasicKeyManager implements IKeyManager { let privateKey = await this.getDecryptedPrivateKey(); const curve = new ec("secp256k1"); const selfKey = curve.keyFromPrivate(privateKey, "hex"); + console.log("Inside DeriveSharedSecret: pubKey, pvtKey", publicKey, privateKey); const userKey = curve.keyFromPublic(publicKey, "hex"); privateKey = "0".repeat(privateKey.length); return selfKey.derive(userKey.getPublic()).toString(16); } public decryptMessage = async (encryptedMessage: string): Promise => { + console.log("Inside DecryptMessage: type, value", typeof encryptedMessage, encryptedMessage); const toDecrypt = BufferJSONSerializer.JSONStringToBufferObject(encryptedMessage); const privateKey = await this.getDecryptedPrivateKey(); const decrypted = await ECIESEncryption.decrypt(toDecrypt, privateKey); diff --git a/src/infrastructure/implementations/blockstack-crux-user-repository.ts b/src/infrastructure/implementations/blockstack-crux-user-repository.ts index b24086e7..8bea59e0 100644 --- a/src/infrastructure/implementations/blockstack-crux-user-repository.ts +++ b/src/infrastructure/implementations/blockstack-crux-user-repository.ts @@ -13,6 +13,7 @@ import { cloneValue } from "../../packages/utils"; import { BlockstackService } from "../services/blockstack-service"; import { GaiaService } from "../services/gaia-service"; import { BlockstackCruxDomainRepository } from "./blockstack-crux-domain-repository"; +import { keyManagementProtocol } from "./crux-messenger"; const log = getLogger(__filename); export interface ICruxpayObject { diff --git a/src/infrastructure/implementations/crux-messenger.ts b/src/infrastructure/implementations/crux-messenger.ts index 232e74ec..faecd6cc 100644 --- a/src/infrastructure/implementations/crux-messenger.ts +++ b/src/infrastructure/implementations/crux-messenger.ts @@ -124,7 +124,7 @@ export class PahoClient implements IPubSubClient { } public connect = () => { - console.log("PahoClient trying to connect"); + console.log("PahoClient trying to connect: ", this.config); if (this.client && this.client.isConnected()) { console.log("Already Connected, returning"); return; @@ -140,7 +140,7 @@ export class PahoClient implements IPubSubClient { console.log("PahoClient - trying to connect"); this.client.connect({ onSuccess: (onSuccessData: any) => { - console.log("PahoClient - connect success!"); + console.log("PahoClient - connect success!", this.config); this.emitter.emit("connectSuccess", onSuccessData); res(onSuccessData); }, @@ -162,7 +162,7 @@ export class PahoClient implements IPubSubClient { } } private onMessageArrived = (msg: any) => { - console.log("recd message from paho library: ", msg.uniqueId, msg); + console.log("recd message from paho library: ", msg.uniqueId, msg, msg.payloadString, msg.destinationName); this.emitter.emit(msg.destinationName, msg.destinationName, msg.payloadString); } private onMessageDelivered = (msg: any) => { @@ -175,7 +175,7 @@ export class CruxNetPubSubClientFactory implements IPubSubClientFactory { private options: ICruxNetClientFactoryOptions; private defaultSubscribeOptions: { qos: number }; private defaultClientMqttOptions: { clean: boolean }; - private bufferPahoClient?: PahoClient; + private bufferPahoClient: any = {}; constructor(options: ICruxNetClientFactoryOptions) { this.options = options; this.defaultSubscribeOptions = { @@ -186,11 +186,12 @@ export class CruxNetPubSubClientFactory implements IPubSubClientFactory { }; } public getClient = (from: CruxId, keyManager: IKeyManager, to?: CruxId): IPubSubClient => { - if (this.bufferPahoClient) { return this.bufferPahoClient; } + const fromCruxIdString = from.toString(); + if (this.bufferPahoClient[fromCruxIdString]) { return this.bufferPahoClient[fromCruxIdString]; } const overrideOpts = this.getDomainLevelClientOptions(to ? to : from); - this.bufferPahoClient = new PahoClient({ + this.bufferPahoClient[fromCruxIdString] = new PahoClient({ clientOptions: { - clientId: from.toString(), + clientId: fromCruxIdString, host: overrideOpts ? overrideOpts.host : this.options.defaultLinkServer.host, path: overrideOpts ? overrideOpts.path : this.options.defaultLinkServer.path, port: overrideOpts ? overrideOpts.port : this.options.defaultLinkServer.port, @@ -198,7 +199,7 @@ export class CruxNetPubSubClientFactory implements IPubSubClientFactory { }, subscribeOptions: this.defaultSubscribeOptions, }); - return this.bufferPahoClient; + return this.bufferPahoClient[fromCruxIdString]; } private getDomainLevelClientOptions = (cruxId: CruxId): {host: string, port: number, path: string} | undefined => { // TODO Implement @@ -221,3 +222,29 @@ export const cruxPaymentProtocol: IMessageSchema[] = [{ .required().min(36).max(36), }), }]; + +export const keyManagementProtocol: IMessageSchema[] = [ + { + messageType: "KEY_MANAGER_REQUEST", + schema: Joi.object({ + args: Joi.array() + .required(), + + invocationId: Joi.string() + .required().min(36).max(36), + + method: Joi.string().valid("signWebToken", "getPubKey", "deriveSharedSecret", "decryptMessage") + .required(), + }), + }, + { + messageType: "KEY_MANAGER_RESPONSE", + schema: Joi.object({ + result: Joi.any() + .required(), + + invocationId: Joi.string() + .required().min(36).max(36), + }), + }, +]; diff --git a/src/samples/remote-crux-settings.html b/src/samples/remote-crux-settings.html new file mode 100644 index 00000000..cae49ad6 --- /dev/null +++ b/src/samples/remote-crux-settings.html @@ -0,0 +1,61 @@ + + + + + + + Remote Crux Settings + + + + +

Demo CruxServiceClient

+ +
CruxPay Interface
+ +
+
Fetch RemoteWalletClient:
+ Remote User Id:
+ +

+    
+ +
+
Fetch address map:
+ +

+    
+ +
+
Publish address map:
+

+ +

+    
+ + ======================== + + + + diff --git a/src/samples/remote-crux-settings.ts b/src/samples/remote-crux-settings.ts new file mode 100644 index 00000000..1c466fd9 --- /dev/null +++ b/src/samples/remote-crux-settings.ts @@ -0,0 +1,178 @@ +import { + CruxWalletClient, + CruxServiceClient, + IAddressMapping, + CruxClientError, + CruxId, + keyManagementProtocol, + InMemStorage +} from "../index"; +import { SecureCruxNetwork, CruxSpec } from "../core"; +import { getCruxUserRepository, getPubsubClientFactory } from "../application"; +import { getCruxdevCruxDomain } from "../test/test-utils"; +import { BasicKeyManager } from "../infrastructure"; +// TODO: add optional import statement to use the build + +const doc = (document as { + getElementById: Function, + getElementsByName: Function, + getElementsByClassName: Function +}) + + + +// Demo wallet artifacts + +let walletClientName = "local" +const btcAddress = "1HX4KvtPdg9QUYwQE1kNqTAjmNaDG7w82V" +const ethAddress = "0x0a2311594059b468c9897338b027c8782398b481" +const trxAddress = "TG3iFaVvUs34SGpWq8RG9gnagDLTe1jdyz" +const xrpAddress = "rpfKAA2Ezqoq5wWo3XENdLYdZ8YGziz48h" +const xrpSecIdentifier = "12345" +const lifeAddress = "0xd26114cd6ee289accf82350c8d8487fedb8a0c07" +const zrxAddress = "0xd26114cd6ee289accf82350c8d8487fedb8a0c07" + +const sampleAddressMaps: {[walletClientName: string]: IAddressMapping} = { + "local,guarda": { + btc: { + addressHash: btcAddress + }, + eth: { + addressHash: ethAddress + }, + trx: { + addressHash: trxAddress + }, + xrp: { + addressHash: xrpAddress, + secIdentifier: xrpSecIdentifier + }, + life: { + addressHash: lifeAddress, + }, + }, + "zel_dev": { + bitcoin: { + addressHash: btcAddress + }, + ethereum: { + addressHash: ethAddress + }, + tron: { + addressHash: trxAddress + }, + ripple: { + addressHash: xrpAddress, + secIdentifier: xrpSecIdentifier + }, + zrx: { + addressHash: zrxAddress, + } + } +}; + +const sampleAddressMap = sampleAddressMaps[Object.keys(sampleAddressMaps).find((keyString) => keyString.split(',').includes(walletClientName))]; +console.log(sampleAddressMap, sampleAddressMaps); +doc.getElementById('publishAddresses').innerHTML = Object.keys(sampleAddressMap).map((currency) => { let address = sampleAddressMap[currency].addressHash; let secIdentifier = sampleAddressMap[currency].secIdentifier; return `${currency.toUpperCase()}` }).join('\n') + + +// --- @crux/js-sdk integration --- // +const userCruxId = CruxId.fromString("release020@cruxdev.crux"); +const userPvtKey = "KyBuSe1MMV6NjJgZfyWuvgmxUeAYswLC2HrfYLUri9aP3AS5FBfr"; +const userBasicKeyManager = new BasicKeyManager(userPvtKey); + +const selfIdClaim = { + cruxId: userCruxId, + keyManager: userBasicKeyManager +} + +const secureCruxNetwork = new SecureCruxNetwork(getCruxUserRepository({ + blockstackInfrastructure: CruxSpec.blockstack.infrastructure, + cruxDomain: getCruxdevCruxDomain(), + cacheStorage: new InMemStorage(), +}), getPubsubClientFactory(), selfIdClaim); + +const cruxServiceClient = new CruxServiceClient({ + cruxId: userCruxId, + keyManager: userBasicKeyManager +}, secureCruxNetwork, keyManagementProtocol); + +let remoteWalletClient: CruxWalletClient; + +const getRemoteWalletClient = async () => { + let UIResponse: string = "Fetching RemoteWalletClient..." + doc.getElementById('getRemoteWalletClientAcknowledgement').textContent = UIResponse; + let remoteUserId = CruxId.fromString(doc.getElementById('remoteUserId').value); + try{ + remoteWalletClient = await cruxServiceClient.getWalletClientForUser(remoteUserId); + UIResponse = `RemoteWalletClient has been initialized for: ${remoteUserId}` + } catch (e) { + console.log(e); + if (e instanceof CruxClientError) { + UIResponse = `${e.errorCode}: ${e}` + } else { + UIResponse = e + } + } finally { + doc.getElementById('getRemoteWalletClientAcknowledgement').textContent = UIResponse + } +} + +const getAddressMap = async () => { + let UIResponse: string = "fetching addressMap remotely..." + doc.getElementById('getAddressMapAcknowledgement').textContent = UIResponse; + try { + let addressMap = await remoteWalletClient.getAddressMap() + UIResponse = JSON.stringify(addressMap, undefined, 4) + } catch (e) { + if (e instanceof CruxClientError) { + UIResponse = `${e.errorCode}: ${e}` + } else { + UIResponse = e + } + } finally { + doc.getElementById('getAddressMapAcknowledgement').textContent = UIResponse + } +} + +const putAddressMap = async () => { + let UIResponse: string = "Publishing your selected addresses remotely..." + let addressMap: IAddressMapping = {}; + // @ts-ignore + [].forEach.call(doc.getElementsByName('publishAddressOption'), (el: HTMLInputElement) => { + if (el.checked) { + addressMap[el.attributes['currency'].nodeValue] = { + addressHash: el.attributes['addressHash'].nodeValue, + secIdentifier: el.attributes['secIdentifier'].nodeValue === "undefined" ? undefined : el.attributes['secIdentifier'].nodeValue + } + } + }); + doc.getElementById('putAddressMapAcknowledgement').textContent = UIResponse + try { + let {success, failures} = await remoteWalletClient.putAddressMap(addressMap) + UIResponse = `successfully published: ${JSON.stringify(success)}, \nFailed publishing: ${JSON.stringify(failures, undefined, 4)}` + } catch (e) { + if (e instanceof CruxClientError) { + UIResponse = `${e.errorCode}: ${e}` + } else { + UIResponse = e + } + } finally { + doc.getElementById('putAddressMapAcknowledgement').textContent = UIResponse + } +} + +// Declaring global variables to be accessible for (button clicks or debugging purposes) +declare global { + interface Window { + remoteWalletClient: CruxWalletClient; + getRemoteWalletClient: Function; + getAddressMap: Function; + putAddressMap: Function; + } +} + +window.remoteWalletClient = remoteWalletClient; +window.getRemoteWalletClient = getRemoteWalletClient; +window.getAddressMap = getAddressMap; +window.putAddressMap = putAddressMap; diff --git a/src/test/crux-messenger/test-crux-service-client.ts b/src/test/crux-messenger/test-crux-service-client.ts new file mode 100644 index 00000000..6fba21db --- /dev/null +++ b/src/test/crux-messenger/test-crux-service-client.ts @@ -0,0 +1,89 @@ +import * as chai from "chai"; +import sinon from "sinon"; +import chaiAsPromised from "chai-as-promised"; +import 'mocha'; +import * as cwc from "../../application/clients/crux-wallet-client"; +import {SecureCruxNetwork, RemoteKeyClient, RemoteKeyHost, RemoteKeyManager} from "../../core/domain-services"; +import {BasicKeyManager, keyManagementProtocol} from "../../infrastructure/implementations"; +import {CruxId, BufferJSONSerializer, InMemStorage} from "../../packages"; +import {InMemoryCruxUserRepository, MockUserStore, patchMissingDependencies, InMemoryCruxDomainRepository, getSomewalletDomain, addUserToRepo, addDomainToRepo} from "../test-utils"; +import {InMemoryPubSubClientFactory, InMemoryMaliciousPubSubClientFactory} from "./inmemory-implementations"; +import {getMockUserBar123CSTestWallet, getMockUserFoo123CSTestWallet, getCstestwalletCruxDomain} from "./utils"; +import { ECIESEncryption } from "../../packages/encryption"; +import { CruxWalletClient, CruxServiceClient } from "../../application/clients"; + +patchMissingDependencies(); + +chai.use(chaiAsPromised); +chai.should(); +const expect = require('chai').expect; + +describe('Test CruxServiceClient - InMemory', function() { + beforeEach(async function() { + const userStore = new MockUserStore(); + const user1Data = getMockUserFoo123CSTestWallet(); + const user2Data = getMockUserBar123CSTestWallet(); + const testCruxDomain = getCstestwalletCruxDomain(); + this.user1Data = user1Data; + this.user2Data = user2Data; + userStore.store(user1Data.cruxUser); + userStore.store(user2Data.cruxUser); + this.inmemUserRepo = new InMemoryCruxUserRepository(userStore, testCruxDomain); + this.inmemDomainRepo = new InMemoryCruxDomainRepository(); + this.inmemDomainRepo = await addDomainToRepo(testCruxDomain, this.inmemDomainRepo); + this.pubsubClientFactory = new InMemoryPubSubClientFactory(); + this.user1KeyManager = new BasicKeyManager(this.user1Data.pvtKey); + this.user2KeyManager = new BasicKeyManager(this.user2Data.pvtKey); + const toEncrypt = Buffer.from("content", "utf8"); + const encrypted = await ECIESEncryption.encrypt(toEncrypt, this.user2KeyManager.publicKey); + this.testEncryptedData = BufferJSONSerializer.bufferObjectToJSONString(encrypted); + this.stubGetCruxDomainRepository = sinon.stub(cwc, 'getCruxDomainRepository').callsFake(() => this.inmemDomainRepo as any); + this.stubGetCruxUserRepository = sinon.stub(cwc, 'getCruxUserRepository').callsFake(() => this.inmemUserRepo as any); + this.stubGetPubsubClientFactory = sinon.stub(cwc, 'getPubsubClientFactory').callsFake(() => this.pubsubClientFactory as any); + this.secureCruxNetwork2 = new SecureCruxNetwork(this.inmemUserRepo, this.pubsubClientFactory, { + cruxId: this.user2Data.cruxUser.cruxID, + keyManager: new BasicKeyManager(this.user2Data.pvtKey) + }); + }); + + afterEach(function() { + this.stubGetCruxUserRepository.restore(); + this.stubGetCruxDomainRepository.restore(); + this.stubGetPubsubClientFactory.restore(); + }); + + it('Send Receive - CruxWalletClient<->CruxServiceClient - getAddressMap', async function() { + const cruxWalletClient = new CruxWalletClient({ + privateKey: this.user1Data.pvtKey, + walletClientName: "cstestwallet", + cacheStorage: new InMemStorage(), + }); + const cruxServiceClient = new CruxServiceClient({ + cruxId: this.user2Data.cruxUser.cruxID, + keyManager: this.user2KeyManager + }, this.secureCruxNetwork2, keyManagementProtocol); + + const remoteWalletClient = await cruxServiceClient.getWalletClientForUser(this.user1Data.cruxUser.cruxID); + const addressMap = await remoteWalletClient.getAddressMap(); + console.log("TESTCASE::AddressMap: ", addressMap); + }); + + it('Send Receive - CruxWalletClient<->CruxServiceClient - putAddressMap', async function() { + const cruxWalletClient = new CruxWalletClient({ + privateKey: this.user1Data.pvtKey, + walletClientName: "cstestwallet", + cacheStorage: new InMemStorage(), + }); + const cruxServiceClient = new CruxServiceClient({ + cruxId: this.user2Data.cruxUser.cruxID, + keyManager: this.user2KeyManager + }, this.secureCruxNetwork2, keyManagementProtocol); + const remoteWalletClient = await cruxServiceClient.getWalletClientForUser(this.user1Data.cruxUser.cruxID); + const addressMapResult = await remoteWalletClient.putAddressMap({ + "bitcoin": { + addressHash: "d78c26f8-7c13-4909-bf62-57d7623f8ee8" + } + }); + console.log("TESTCASE::Address Map: ", addressMapResult); + }); +}) \ No newline at end of file diff --git a/src/test/crux-messenger/test-key-management-protocol.ts b/src/test/crux-messenger/test-key-management-protocol.ts new file mode 100644 index 00000000..fe70e2e2 --- /dev/null +++ b/src/test/crux-messenger/test-key-management-protocol.ts @@ -0,0 +1,275 @@ +import * as chai from "chai"; +import sinon from "sinon"; +import chaiAsPromised from "chai-as-promised"; +import 'mocha'; +import {SecureCruxNetwork, CertificateManager, CruxProtocolMessenger} from "../../core/domain-services"; +import {ICruxUserRepository, IProtocolMessage, IPubSubClientFactory} from "../../core/interfaces"; +import {BasicKeyManager, cruxPaymentProtocol, keyManagementProtocol} from "../../infrastructure/implementations"; +import {CruxId} from "../../packages"; +import {InMemoryCruxUserRepository, MockUserStore, patchMissingDependencies} from "../test-utils"; +import {InMemoryPubSubClientFactory, InMemoryMaliciousPubSubClientFactory} from "./inmemory-implementations"; +import {getMockUserBar123CSTestWallet, getMockUserFoo123CSTestWallet, getMockUserFooBar123CSTestWallet} from "./utils"; + +patchMissingDependencies(); + +chai.use(chaiAsPromised); +chai.should(); +const expect = require('chai').expect; + + + +describe('Test Key Management Protocol', function() { + beforeEach(async function() { + const userStore = new MockUserStore(); + const user1Data = getMockUserFoo123CSTestWallet(); + const user2Data = getMockUserBar123CSTestWallet(); + const user3Data = getMockUserFooBar123CSTestWallet(); + this.user1Data = user1Data; + this.user2Data = user2Data; + this.user3Data = user3Data; + userStore.store(user1Data.cruxUser); + userStore.store(user2Data.cruxUser); + const inmemUserRepo = new InMemoryCruxUserRepository(userStore); + const pubsubClientFactory = new InMemoryPubSubClientFactory(); + const user1Messenger = new SecureCruxNetwork(inmemUserRepo, pubsubClientFactory, { + cruxId: this.user1Data.cruxUser.cruxID, + keyManager: new BasicKeyManager(this.user1Data.pvtKey) + }); + const user2Messenger = new SecureCruxNetwork(inmemUserRepo, pubsubClientFactory, { + cruxId: this.user2Data.cruxUser.cruxID, + keyManager: new BasicKeyManager(this.user2Data.pvtKey) + }); + await user1Messenger.initialize(); + await user2Messenger.initialize(); + this.user1KeyManagerProtocolMessenger = new CruxProtocolMessenger(user1Messenger, keyManagementProtocol); + this.user2KeyManagerProtocolMessenger = new CruxProtocolMessenger(user2Messenger, keyManagementProtocol); + + }); + + describe('Test Key Management Protocol - (Request)', function() { + it('Valid Key Manager Request - Get Public Key', async function() { + return new Promise(async (res, rej) => { + const validKeyManagerRequest = { + args: [], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4', + method: "getPubKey" + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_REQUEST", + content: validKeyManagerRequest + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_REQUEST', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerRequest); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + it('Valid Key Manager Request - Sign Web Token', async function() { + return new Promise(async (res, rej) => { + const validKeyManagerRequest = { + args: ['webtoken'], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4', + method: "signWebToken" + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_REQUEST", + content: validKeyManagerRequest + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_REQUEST', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerRequest); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + it('Valid Key Manager Request - Derive Shared Secret', async function() { + const testPubKey = "03e6bbc79879d37473836771441a79d3d9dddfabacdac22ed315e5636ff819a318"; + return new Promise(async (res, rej) => { + const validKeyManagerRequest = { + args: [testPubKey], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4', + method: "deriveSharedSecret" + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_REQUEST", + content: validKeyManagerRequest + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_REQUEST', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerRequest); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + it('Valid Key Manager Request - Decrypt Message', async function() { + const testEncryptedMessage = "4b4f34746f434c30354349312b41314b554f644542773d3d" + return new Promise(async (res, rej) => { + const validKeyManagerRequest = { + args: [testEncryptedMessage], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4', + method: "decryptMessage" + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_REQUEST", + content: validKeyManagerRequest + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_REQUEST', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerRequest); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + it('Invalid Key Manager Request - Wrong method', async function() { + const invalidKeyManagerRequest = { + args: ['token'], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4', + method: "getPublicKey" + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_REQUEST", + content: invalidKeyManagerRequest + } + const promise = this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID); + return expect(promise).to.be.eventually.rejected; + }); + }); + + describe('Test Key Management Protocol - (Response)', function() { + it('Valid Key Manager Response - String output', async function() { + return new Promise(async (res, rej) => { + const validKeyManagerResponse = { + result: "03e6bbc79879d37473836771441a79d3d9dddfabacdac22ed315e5636ff819a318", + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4', + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_RESPONSE", + content: validKeyManagerResponse + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_RESPONSE', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerResponse); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + it('Valid Key Manager Response - Object Output', async function() { + return new Promise(async (res, rej) => { + const validKeyManagerResponse = { + result: [{ + 'webtoken': "hello" + }], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4' + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_RESPONSE", + content: validKeyManagerResponse + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_RESPONSE', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerResponse); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + it('Valid Key Manager Response - Array Output', async function() { + return new Promise(async (res, rej) => { + const validKeyManagerResponse = { + result: ['webtoken'], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4' + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_RESPONSE", + content: validKeyManagerResponse + } + const that = this; + this.user2KeyManagerProtocolMessenger.on('KEY_MANAGER_RESPONSE', async (msg: any, senderId?: CruxId)=>{ + try { + expect(msg).to.deep.equal(validKeyManagerResponse); + expect(senderId!.toString()).equals(that.user1Data.cruxUser.cruxID.toString()); + } catch (e) { + rej(e) + } + res() + }, (err: any)=>{ + rej(err) + }); + await this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID) + }); + }); + + it('Invalid Key Manager Response - Missing data', async function() { + const validKeyManagerResponse = { + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a052b7caa4' + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_RESPONSE", + content: validKeyManagerResponse + } + const promise = this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID); + return expect(promise).to.be.eventually.rejected; + }); + it('Invalid Key Manager Response - invocationId wrong length', async function() { + const validKeyManagerResponse = { + result: ['webtoken'], + invocationId: '7c3baa3c-f5e8-490a-88a1-e0a02b7caa4' + } + const testMessage: IProtocolMessage = { + type: "KEY_MANAGER_RESPONSE", + content: validKeyManagerResponse + } + const promise = this.user1KeyManagerProtocolMessenger.send(testMessage, this.user2Data.cruxUser.cruxID); + return expect(promise).to.be.eventually.rejected; + }); + }); + +}) diff --git a/src/test/crux-messenger/test-remote-key-client.ts b/src/test/crux-messenger/test-remote-key-client.ts new file mode 100644 index 00000000..fe79f43c --- /dev/null +++ b/src/test/crux-messenger/test-remote-key-client.ts @@ -0,0 +1,91 @@ +import * as chai from "chai"; +import sinon from "sinon"; +import chaiAsPromised from "chai-as-promised"; +import 'mocha'; +import {SecureCruxNetwork, RemoteKeyClient, RemoteKeyHost, RemoteKeyManager, CruxProtocolMessenger} from "../../core/domain-services"; +import {BasicKeyManager, keyManagementProtocol} from "../../infrastructure/implementations"; +import {CruxId, BufferJSONSerializer} from "../../packages"; +import {InMemoryCruxUserRepository, MockUserStore, patchMissingDependencies} from "../test-utils"; +import {InMemoryPubSubClientFactory, InMemoryMaliciousPubSubClientFactory} from "./inmemory-implementations"; +import {getMockUserBar123CSTestWallet, getMockUserFoo123CSTestWallet, getMockUserFooBar123CSTestWallet} from "./utils"; +import { ECIESEncryption } from "../../packages/encryption"; + +patchMissingDependencies(); + +chai.use(chaiAsPromised); +chai.should(); +const expect = require('chai').expect; + + +describe('Test RemoteKeyClient', function() { + beforeEach(async function() { + const userStore = new MockUserStore(); + const user1Data = getMockUserFoo123CSTestWallet(); + const user2Data = getMockUserBar123CSTestWallet(); + // const user3Data = getMockUserFooBar123CSTestWallet(); + this.user1Data = user1Data; + this.user2Data = user2Data; + // this.user3Data = user3Data; + userStore.store(user1Data.cruxUser); + userStore.store(user2Data.cruxUser); + this.inmemUserRepo = new InMemoryCruxUserRepository(userStore); + this.pubsubClientFactory = new InMemoryPubSubClientFactory(); + this.user1KeyManager = new BasicKeyManager(this.user1Data.pvtKey); + this.user2KeyManager = new BasicKeyManager(this.user2Data.pvtKey); + const toEncrypt = Buffer.from("content", "utf8"); + const encrypted = await ECIESEncryption.encrypt(toEncrypt, this.user2KeyManager.publicKey); + this.testEncryptedData = BufferJSONSerializer.bufferObjectToJSONString(encrypted); + this.secureCruxNetwork1 = new SecureCruxNetwork(this.inmemUserRepo, this.pubsubClientFactory, { + cruxId: this.user1Data.cruxUser.cruxID, + keyManager: this.user1KeyManager + }); + this.secureCruxNetwork2 = new SecureCruxNetwork(this.inmemUserRepo, this.pubsubClientFactory, { + cruxId: this.user2Data.cruxUser.cruxID, + keyManager: this.user2KeyManager + }); + this.cruxProtocolMessenger1 = new CruxProtocolMessenger(this.secureCruxNetwork1, keyManagementProtocol); + this.cruxProtocolMessenger2 = new CruxProtocolMessenger(this.secureCruxNetwork2, keyManagementProtocol); + await this.cruxProtocolMessenger1.initialize(); + await this.cruxProtocolMessenger2.initialize(); + }); + + it('Basic Key Manager Send Receive', async function() { + const testPublicKey = '03e6bbc79879d37473836771441a79d3d9dddfabacdac22ed315e5636ff819a318'; + return new Promise(async (resolve, reject) => { + const remoteKeyClient = new RemoteKeyClient(this.cruxProtocolMessenger1, this.user2Data.cruxUser.cruxID); + await remoteKeyClient.initialize(); + const remoteKeyHost = new RemoteKeyHost(this.cruxProtocolMessenger2, this.user2KeyManager); + await remoteKeyHost.initialize() + const invocationId = await remoteKeyClient.invoke("getPubKey", []); + remoteKeyClient.listenToInvocation(invocationId, (msg, senderId) => { + expect(msg.result.data).equals(testPublicKey); + resolve(msg) + },(err) => { + reject(err) + }); + }); + }); + + it('Basic Key Manager Send Receive - RemoteKeyManager', async function() { + const testPubKey = "03e6bbc79879d37473836771441a79d3d9dddfabacdac22ed315e5636ff819a318"; + return new Promise(async (resolve, reject) => { + const remoteKeyManager = new RemoteKeyManager(this.cruxProtocolMessenger1, this.user2Data.cruxUser.cruxID); + const remoteKeyHost = new RemoteKeyHost(this.cruxProtocolMessenger2, this.user2KeyManager); + await remoteKeyManager.initialize(); + await remoteKeyHost.initialize(); + const signedWebToken = await remoteKeyManager.signWebToken("1234567") + console.log(signedWebToken); + expect(signedWebToken).to.have.length(138); + const publicKey = await remoteKeyManager.getPubKey(); + console.log(publicKey); + expect(publicKey).to.equals(testPubKey); + const sharedSecret = await remoteKeyManager.deriveSharedSecret(testPubKey) + console.log(sharedSecret); + expect(sharedSecret).to.equals("3380b4752c9cebf96bc55491ef0ee67ae1d564c0bb931a0c6e8875be6e3bee5"); + const decryptedMessage = await remoteKeyManager.decryptMessage(this.testEncryptedData); + console.log(decryptedMessage); + expect(decryptedMessage).to.equals("content"); + resolve(); + }); + }); +}) diff --git a/src/test/crux-messenger/utils.ts b/src/test/crux-messenger/utils.ts index 1adb0cc5..545bab1b 100644 --- a/src/test/crux-messenger/utils.ts +++ b/src/test/crux-messenger/utils.ts @@ -9,7 +9,7 @@ import { } from "../../core/entities"; import {CruxDomainId, CruxId, getKeyPairFromPrivKey} from "../../packages"; -const getCstestwalletCruxDomain = () => { +export const getCstestwalletCruxDomain = () => { const testCruxDomainId = CruxDomainId.fromString('cstestwallet.crux'); const domainStatus: DomainRegistrationStatus = DomainRegistrationStatus.REGISTERED; const testValidDomainAssetMapping = { diff --git a/src/test/integration-tests/crux-messenger/paho-pubsub-client-runner.ts b/src/test/integration-tests/crux-messenger/paho-pubsub-client-runner.ts index 8dea9c82..8eb983ed 100644 --- a/src/test/integration-tests/crux-messenger/paho-pubsub-client-runner.ts +++ b/src/test/integration-tests/crux-messenger/paho-pubsub-client-runner.ts @@ -11,7 +11,7 @@ patchMissingDependencies() chai.use(chaiAsPromised); chai.should(); const expect = require('chai').expect; -const BROKER_HOST = "broker.hivemq.com"; +const BROKER_HOST = "127.0.0.1"; const BROKER_PORT = 8000; const path = '/mqtt' diff --git a/src/test/integration-tests/crux-messenger/secure-cruxid-messenger-prod-test.ts b/src/test/integration-tests/crux-messenger/secure-cruxid-messenger-prod-test.ts index 8168a644..4406653e 100644 --- a/src/test/integration-tests/crux-messenger/secure-cruxid-messenger-prod-test.ts +++ b/src/test/integration-tests/crux-messenger/secure-cruxid-messenger-prod-test.ts @@ -21,7 +21,7 @@ const user2PvtKey = "cdf2d276caf0c9c34258ed6ebd0e60e0e8b3d9a7b8a9a717f2e19ed9b37 describe('Test Secure Crux Messenger - PROD', function() { beforeEach(async function() { - const HOST = "broker.hivemq.com"; + const HOST = "127.0.0.1"; const PORT = 8000; const path = '/mqtt'; this.user1KeyManager = new BasicKeyManager(user1PvtKey); diff --git a/src/test/integration-tests/crux-messenger/test-crux-service-client-prod.ts b/src/test/integration-tests/crux-messenger/test-crux-service-client-prod.ts new file mode 100644 index 00000000..8e6e41e4 --- /dev/null +++ b/src/test/integration-tests/crux-messenger/test-crux-service-client-prod.ts @@ -0,0 +1,85 @@ +import * as chai from "chai"; +import * as blockstack from "blockstack"; +import sinon from "sinon"; +import chaiAsPromised from "chai-as-promised"; +import 'mocha'; +import {SecureCruxNetwork} from "../../../core/domain-services"; +import {CruxServiceClient, getCruxUserRepository, CruxWalletClient} from "../../../application/clients" +import {BasicKeyManager, CruxNetPubSubClientFactory, keyManagementProtocol} from "../../../infrastructure/implementations"; +import {patchMissingDependencies, getCruxdevCruxDomain} from "../../test-utils"; +import { CruxSpec, IAddress } from "../../../core/entities"; +import { CruxId, InMemStorage } from "../../../packages"; +import { bip32 } from "bitcoinjs-lib"; +import * as bip39 from "bip39"; + +patchMissingDependencies(); + +chai.use(chaiAsPromised); +chai.should(); +const expect = require('chai').expect; + +const btcAddress: IAddress = {addressHash: "1HX4KvtPdg9QUYwQE1kNqTAjmNaDG7w82V"}; +const user1PvtKey = "cdf2d276caf0c9c34258ed6ebd0e60e0e8b3d9a7b8a9a717f2e19ed9b37f7c6f"; // mascot6699@cruxdex.crux +const user2PvtKey = "KyBuSe1MMV6NjJgZfyWuvgmxUeAYswLC2HrfYLUri9aP3AS5FBfr"; // release020@cruxdev.crux + +describe('Test CruxServiceClient - Prod', function() { + beforeEach(async function() { + const HOST = "127.0.0.1"; + const PORT = 8000; + this.user2CruxId = "release020@cruxdev.crux"; + this.user1KeyManager = new BasicKeyManager(user1PvtKey); + this.user2KeyManager = new BasicKeyManager(user2PvtKey); + this.userRepo = getCruxUserRepository({ + blockstackInfrastructure: CruxSpec.blockstack.infrastructure, + cruxDomain: getCruxdevCruxDomain(), + cacheStorage: new InMemStorage(), + }); + this.user1Data = await this.userRepo.getWithKey(this.user1KeyManager); + this.user2Data = await this.userRepo.getWithKey(this.user2KeyManager); + this.pubsubClientFactory = new CruxNetPubSubClientFactory({ + defaultLinkServer: { + host: HOST, + port: PORT, + path: "/mqtt" + } + }); + this.secureCruxNetwork2 = new SecureCruxNetwork(this.userRepo, this.pubsubClientFactory, { + cruxId: this.user2Data.cruxID, + keyManager: new BasicKeyManager(user2PvtKey) + }); + }); + it('Send Receive - CruxWalletClient<->CruxServiceClient - getAddressMap', async function() { + const cruxWalletClient = new CruxWalletClient({ + privateKey: user1PvtKey, + walletClientName: "cruxdev", + cacheStorage: new InMemStorage(), + }); + const cruxServiceClient = new CruxServiceClient({ + cruxId: CruxId.fromString(this.user2CruxId), + keyManager: this.user2KeyManager + }, this.secureCruxNetwork2, keyManagementProtocol); + + const remoteWalletClient = await cruxServiceClient.getWalletClientForUser(this.user1Data.cruxID); + const addressMap = await remoteWalletClient.getAddressMap(); + console.log("TESTCASE::Get Address Map: ", addressMap); + }); + + it('Send Receive - CruxWalletClient<->CruxServiceClient - putAddressMap', async function() { + const cruxWalletClient = new CruxWalletClient({ + privateKey: user1PvtKey, + walletClientName: "cruxdev", + cacheStorage: new InMemStorage(), + }); + const cruxServiceClient = new CruxServiceClient({ + cruxId: CruxId.fromString(this.user2CruxId), + keyManager: this.user2KeyManager + }, this.secureCruxNetwork2, keyManagementProtocol); + const remoteWalletClient = await cruxServiceClient.getWalletClientForUser(this.user1Data.cruxID); + const addressMapResult = await remoteWalletClient.putAddressMap({ + "ltc": { + addressHash: "1HX4KvtPdg9QUYwQE1kNqTAjmNaDG12346" + } + }); + console.log("TESTCASE::Put Address Map: ", addressMapResult); + }); +}); \ No newline at end of file diff --git a/src/test/integration-tests/crux-messenger/test-remote-key-client-prod.ts b/src/test/integration-tests/crux-messenger/test-remote-key-client-prod.ts new file mode 100644 index 00000000..78ffc5ff --- /dev/null +++ b/src/test/integration-tests/crux-messenger/test-remote-key-client-prod.ts @@ -0,0 +1,103 @@ +import * as chai from "chai"; +import sinon from "sinon"; +import chaiAsPromised from "chai-as-promised"; +import 'mocha'; +import * as blockstack from "blockstack"; +import {RemoteKeyClient, RemoteKeyHost, RemoteKeyManager, SecureCruxNetwork, CruxProtocolMessenger} from "../../../core/domain-services"; +import {BasicKeyManager, CruxNetPubSubClientFactory, keyManagementProtocol} from "../../../infrastructure/implementations"; +import {CruxId, InMemStorage, BufferJSONSerializer} from "../../../packages"; +import {InMemoryCruxUserRepository, MockUserStore, patchMissingDependencies, getCruxdevCruxDomain} from "../../test-utils"; +import { bip32 } from "bitcoinjs-lib"; +import * as bip39 from "bip39"; +import { getCruxUserRepository } from "../../../application/clients"; +import { CruxSpec } from "../../../core/entities"; +import { ECIESEncryption } from "../../../packages/encryption"; + +patchMissingDependencies(); + +chai.use(chaiAsPromised); +chai.should(); +const expect = require('chai').expect; + +const user1PvtKey = "cdf2d276caf0c9c34258ed6ebd0e60e0e8b3d9a7b8a9a717f2e19ed9b37f7c6f"; // mascot6699@cruxdex.crux +const user2PvtKey = "KyBuSe1MMV6NjJgZfyWuvgmxUeAYswLC2HrfYLUri9aP3AS5FBfr"; // release020@cruxdev.crux + +describe('Test RemoteKeyClient - PROD', function() { + beforeEach(async function() { + const HOST = "127.0.0.1"; + const PORT = 8000; + this.user2CruxId = "release020@cruxdev.crux"; + this.user1KeyManager = new BasicKeyManager(user1PvtKey); + this.user2KeyManager = new BasicKeyManager(user2PvtKey); + this.userRepo = getCruxUserRepository({ + blockstackInfrastructure: CruxSpec.blockstack.infrastructure, + cruxDomain: getCruxdevCruxDomain(), + cacheStorage: new InMemStorage(), + }); + this.user1Data = await this.userRepo.getWithKey(this.user1KeyManager); + this.user2Data = await this.userRepo.getWithKey(this.user2KeyManager); + this.pubsubClientFactory = new CruxNetPubSubClientFactory({ + defaultLinkServer: { + host: HOST, + port: PORT, + path: "/mqtt" + } + }); + const toEncrypt = Buffer.from("content", "utf8"); + const encrypted = await ECIESEncryption.encrypt(toEncrypt, "0362f171a40ab5e6ad22275ec166f15a232b83a571bab9c30622ed2963f1da4c08"); + this.testEncryptedData = BufferJSONSerializer.bufferObjectToJSONString(encrypted); + this.secureCruxNetwork1 = new SecureCruxNetwork(this.userRepo, this.pubsubClientFactory, { + cruxId: this.user1Data.cruxID, + keyManager: this.user1KeyManager + }); + this.secureCruxNetwork2 = new SecureCruxNetwork(this.userRepo, this.pubsubClientFactory, { + cruxId: this.user2Data.cruxID, + keyManager: this.user2KeyManager + }); + this.cruxProtocolMessenger1 = new CruxProtocolMessenger(this.secureCruxNetwork1, keyManagementProtocol); + this.cruxProtocolMessenger2 = new CruxProtocolMessenger(this.secureCruxNetwork2, keyManagementProtocol); + await this.cruxProtocolMessenger1.initialize(); + await this.cruxProtocolMessenger2.initialize(); + }); + + it('Send Receive RemoteKeyClient<->RemoteKeyHost - Prod', async function() { + const testPublicKey = '0239d9d97d5b8973fba462b1a014bcbb84d056061234fd375442a7ef0620ea88c3'; + return new Promise(async (resolve, reject) => { + const remoteKeyClient = new RemoteKeyClient(this.cruxProtocolMessenger1, this.user2Data.cruxID); + await remoteKeyClient.initialize(); + const remoteKeyHost = new RemoteKeyHost(this.cruxProtocolMessenger2, this.user2KeyManager); + await remoteKeyHost.initialize() + const invocationId = await remoteKeyClient.invoke("getPubKey", []); + remoteKeyClient.listenToInvocation(invocationId, (msg, senderId) => { + console.log("TESTCASE::msg.result.data", msg.result.data); + expect(msg.result.data).equals(testPublicKey); + resolve(msg) + },(err) => { + reject(err) + }); + }); + }); + + it('Send Receive RemoteKeyManager<->RemoteKeyHost - RemoteKeyManager - Prod', async function() { + const testPubKey = "0362f171a40ab5e6ad22275ec166f15a232b83a571bab9c30622ed2963f1da4c08"; + return new Promise(async (resolve, reject) => { + const remoteKeyManager = new RemoteKeyManager(this.cruxProtocolMessenger2, this.user1Data.cruxID); + await remoteKeyManager.initialize(); + const remoteKeyHost = new RemoteKeyHost(this.cruxProtocolMessenger1, this.user1KeyManager); + await remoteKeyHost.initialize(); + const signedWebToken = await remoteKeyManager.signWebToken("1234567") + console.log("TESTCASE::signWebToken:", signedWebToken); + expect(signedWebToken).to.have.length(138); + const publicKey = await remoteKeyManager.getPubKey(); + console.log("TESTCASE::publicKey", publicKey); + expect(publicKey).equals(testPubKey); + const sharedSecret = await remoteKeyManager.deriveSharedSecret(testPubKey) + console.log("TESTCASE::sharedSecret", sharedSecret); + expect(sharedSecret).equals("d2744fdfa47538b816623e75cc783469cbe3d71da02965edd662ae3f45fbac3"); + const decryptedMessage = await remoteKeyManager.decryptMessage(this.testEncryptedData); + console.log("TESTCASE::decryptedMessage", decryptedMessage); + expect(decryptedMessage).equals("content"); + resolve(); + }); + }); +}) diff --git a/src/test/integration-tests/crux-messenger/test-secure-cruxid-messenger-prod-pubsubClientFactory.ts b/src/test/integration-tests/crux-messenger/test-secure-cruxid-messenger-prod-pubsubClientFactory.ts index 3d024bbd..95fb501f 100644 --- a/src/test/integration-tests/crux-messenger/test-secure-cruxid-messenger-prod-pubsubClientFactory.ts +++ b/src/test/integration-tests/crux-messenger/test-secure-cruxid-messenger-prod-pubsubClientFactory.ts @@ -2,7 +2,7 @@ import * as chai from "chai"; import sinon from "sinon"; import chaiAsPromised from "chai-as-promised"; import 'mocha'; -import {SecureCruxIdMessenger, CertificateManager} from "../../../core/domain-services"; +import {SecureCruxNetwork, CertificateManager} from "../../../core/domain-services"; import {BasicKeyManager, CruxNetPubSubClientFactory} from "../../../infrastructure/implementations"; import {InMemoryCruxUserRepository, MockUserStore, patchMissingDependencies} from "../../test-utils"; import {getMockUserBar123CSTestWallet, getMockUserFoo123CSTestWallet} from "../../crux-messenger/utils"; @@ -16,7 +16,7 @@ const expect = require('chai').expect; describe('Test Secure Crux Messenger - Prod pubsubClientFactory', function() { beforeEach(async function() { - const HOST = "broker.hivemq.com"; + const HOST = "127.0.0.1"; const PORT = 8000; const path = '/mqtt'; const userStore = new MockUserStore(); @@ -36,25 +36,25 @@ describe('Test Secure Crux Messenger - Prod pubsubClientFactory', function() { }); }); - it('Basic Send Receive Test - Prod pubsubClientFactory', function() { + it('Basic Send Receive Test - Prod PubsubClientFactory', function() { const testmsg = 'HelloWorld'; return new Promise(async (resolve, reject) => { - const user1Messenger = new SecureCruxIdMessenger(this.inmemUserRepo, this.pubsubClientFactory, { + const user1Messenger = new SecureCruxNetwork(this.inmemUserRepo, this.pubsubClientFactory, { cruxId: this.user1Data.cruxUser.cruxID, keyManager: new BasicKeyManager(this.user1Data.pvtKey) }); - const user2Messenger = new SecureCruxIdMessenger(this.inmemUserRepo, this.pubsubClientFactory, { + const user2Messenger = new SecureCruxNetwork(this.inmemUserRepo, this.pubsubClientFactory, { cruxId: this.user2Data.cruxUser.cruxID, keyManager: new BasicKeyManager(this.user2Data.pvtKey) }); - user2Messenger.listen((msg) => { + await user1Messenger.initialize(); + await user2Messenger.initialize(); + user2Messenger.receive((msg) => { expect(msg).equals(testmsg) resolve(msg) - },(err) => { - reject(err) }); - await user1Messenger.send(testmsg, this.user2Data.cruxUser.cruxID); + await user1Messenger.send(this.user2Data.cruxUser.cruxID, testmsg); }); });