diff --git a/Projects/Network/NT/Modules/WebSocketsInterface.js b/Projects/Network/NT/Modules/WebSocketsInterface.js index a1a286d87..30c56da58 100644 --- a/Projects/Network/NT/Modules/WebSocketsInterface.js +++ b/Projects/Network/NT/Modules/WebSocketsInterface.js @@ -9,12 +9,16 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock let clientInterface let peerInterface + let web3 = new SA.nodeModules.web3() + return thisObject function finalize() { socketServer = undefined clientInterface = undefined peerInterface = undefined + + web3 = undefined } function initialize() { @@ -31,6 +35,8 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock function onConnection(socket) { let caller = {} + let calledTimestamp = (new Date()).valueOf() + socket.on('message', onMenssage) async function onMenssage(message) { @@ -60,7 +66,7 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock switch (messageHeader.messageType) { case "Handshake": { - handshakeProducedure(socket, caller, messageHeader) + handshakeProducedure(socket, caller, calledTimestamp, messageHeader) break } case "Request": { @@ -95,75 +101,213 @@ exports.newNetworkModulesWebSocketsInterface = function newNetworkModulesWebSock } } - function handshakeProducedure(socket, caller, messageHeader) { + function handshakeProducedure(socket, caller, calledTimestamp, messageHeader) { /* - The caller needs to identify itself as either a Network Client or Peer. + The handshage producedure have 2 steps, we need to know + now which one we are at. */ - if (messageHeader.callerRole === undefined) { + if (messageHeader.step === undefined) { let response = { result: 'Error', - message: 'callerRole Not Provided.' + message: 'step Not Provided.' } socket.send(JSON.stringify(response)) return } - - if (messageHeader.callerRole !== 'Network Client' && messageHeader.callerRole !== 'Network Peer') { + if (messageHeader.step !== 'One' && messageHeader.step !== 'Two') { let response = { result: 'Error', - message: 'callerRole Not Supported.' + message: 'step Not Supported.' } socket.send(JSON.stringify(response)) return } + switch (messageHeader.step) { + case 'One': { + handshakeStepOne() + break + } + case 'Two': { + handshakeStepTwo() + break + } + } + function handshakeStepOne() { + /* + The caller needs to identify itself as either a Network Client or Peer. + */ + if (messageHeader.callerRole === undefined) { + let response = { + result: 'Error', + message: 'callerRole Not Provided.' + } + socket.send(JSON.stringify(response)) + return + } + + if (messageHeader.callerRole !== 'Network Client' && messageHeader.callerRole !== 'Network Peer') { + let response = { + result: 'Error', + message: 'callerRole Not Supported.' + } + socket.send(JSON.stringify(response)) + return + } + + caller.role = messageHeader.callerRole + /* + The caller needs to provide it's User Profile Handle. + */ + if (messageHeader.callerProfileHandle === undefined) { + let response = { + result: 'Error', + message: 'callerProfileHandle Not Provided.' + } + socket.send(JSON.stringify(response)) + return + } + + caller.userProfileHandle = messageHeader.callerProfileHandle + /* + The caller needs to provide a callerTimestamp. + */ + if (messageHeader.callerTimestamp === undefined) { + let response = { + result: 'Error', + message: 'callerTimestamp Not Provided.' + } + socket.send(JSON.stringify(response)) + return + } + /* + The callerTimestamp can not be more that 1 minute old. + */ + if (calledTimestamp - messageHeader.callerTimestamp > 60000) { + let response = { + result: 'Error', + message: 'callerTimestamp Too Old.' + } + socket.send(JSON.stringify(response)) + return + } + /* + We will sign for the caller it's handle to prove our + Network Node identity. + */ + let signedMessage = { + callerProfileHandle: messageHeader.callerProfileHandle, + calledProfileHandle: NT.NETWORK_NODE_USER_PROFILE_HANDLE, + callerTimestamp: messageHeader.callerTimestamp, + calledTimestamp: calledTimestamp + } + let signature = web3.eth.accounts.sign(JSON.stringify(signedMessage), NT.NETWORK_NODE_USER_PROFILE_PRIVATE_KEY) - caller.role = messageHeader.callerRole - /* - We will identify the caller by the signature at the message. - */ - if (messageHeader.signature === undefined) { let response = { - result: 'Error', - message: 'signature Not Provided.' + result: 'Ok', + message: 'Handshake Step One Complete', + signature: JSON.stringify(signature) } socket.send(JSON.stringify(response)) - return } - let web3 = new SA.nodeModules.web3() - let blockchainAccount = web3.eth.accounts.recover(messageHeader.signature) + function handshakeStepTwo() { + /* + We will check that the caller role has beed defined at Step One. + */ + if (caller.role === undefined) { + let response = { + result: 'Error', + message: 'Handshake Step One Not Completed.' + } + socket.se + } + /* + We will check the signature at the message. + */ + if (messageHeader.signature === undefined) { + let response = { + result: 'Error', + message: 'signature Not Provided.' + } + socket.send(JSON.stringify(response)) + return + } + + let signature = JSON.parse(messageHeader.signature) + let blockchainAccount = web3.eth.accounts.recover(signature) + + if (caller.blockchainAccount === undefined) { + let response = { + result: 'Error', + message: 'Bad Signature.' + } + socket.send(JSON.stringify(response)) + return + } + /* + The signature gives us the blockchain account, and the account the user profile. + */ + let witnessUserProfile = NT.projects.socialTrading.globals.memory.maps.USER_PROFILES_BY_BLOCHAIN_ACCOUNT.set(blockchainAccount) + + if (witnessUserProfile === undefined) { + let response = { + result: 'Error', + message: 'userProfile Not Found.' + } + socket.send(JSON.stringify(response)) + return + } + /* + The user profile based on the blockchain account, based on the signature, + it is our witness user profile, to validate the caller. + */ + let signedMessage = JSON.parse(signature.message) + + if (signedMessage.callerProfileHandle !== witnessUserProfile.userProfileHandle) { + let response = { + result: 'Error', + message: 'callerProfileHandle Does Not Match witnessUserProfile.' + } + socket.send(JSON.stringify(response)) + return + } + /* + We will check that the signature includes this Network Node handle, to avoid + man in the middle attackts. + */ + if (signedMessage.calledProfileHandle !== NT.NETWORK_NODE_USER_PROFILE_HANDLE) { + let response = { + result: 'Error', + message: 'calledProfileHandle Does Not Match This Network Node Handle.' + } + socket.send(JSON.stringify(response)) + return + } + /* + We will check that the timestamp in the signature is the one we sent to the caller. + */ + if (signedMessage.calledTimestamp !== calledTimestamp) { + let response = { + result: 'Error', + message: 'calledTimestamp Does Not Match calledTimestamp On Record.' + } + socket.send(JSON.stringify(response)) + return + } + /* + All validations have been completed, the Handshake Prcedure finished well. + */ + /* + We will remember the user profile behind this caller. + */ + caller.userProfile = witnessUserProfile - if (caller.blockchainAccount === undefined) { let response = { - result: 'Error', - message: 'Bad signature.' + result: 'Ok', + message: 'Handshake Successful.' } socket.send(JSON.stringify(response)) - return } - /* - The signature gives us the blockchain account, and the account the user profile. - */ - let userProfile = NT.projects.socialTrading.globals.memory.maps.USER_PROFILES_BY_BLOCHAIN_ACCOUNT.set(blockchainAccount) - - if (userProfile === undefined) { - let response = { - result: 'Error', - message: 'userProfile Not Found.' - } - socket.send(JSON.stringify(response)) - return - } - /* - We will remember the user profile behind this caller. - */ - caller.userProfile = userProfile - - let response = { - result: 'Ok', - message: 'Handshake Successful.' - } - socket.send(JSON.stringify(response)) } } catch (err) { diff --git a/Projects/Network/SA/Modules/WebSocketsClient.js b/Projects/Network/SA/Modules/WebSocketsClient.js index 01e53f1ed..ef4b002bf 100644 --- a/Projects/Network/SA/Modules/WebSocketsClient.js +++ b/Projects/Network/SA/Modules/WebSocketsClient.js @@ -9,6 +9,10 @@ exports.newNetworkModulesWebSocketsClient = function newNetworkModulesWebSockets let socketClient let callbackFunction + let web3 = new SA.nodeModules.web3() + let called = {} + let selectedNetworkNode // This is a Network Node we pick to try to connect to. + return thisObject function finalize() { @@ -16,10 +20,25 @@ exports.newNetworkModulesWebSocketsClient = function newNetworkModulesWebSockets socketClient = undefined networkInterface = undefined peerInterface = undefined + + web3 = undefined + called = undefined } function initialize() { - socketClient = new SA.nodeModules.ws('ws://' + 'localhost' + ':' + global.env.NETWORK_WEB_SOCKETS_INTERFACE_PORT) + /* + Here we will pick a Network Node from all users profiles available that do have a Network Node running. // TODO + */ + selectedNetworkNode = { + userProfileId: "qeqdqxex1qexqexqwxeqweqxeqweqe", + userProfileHandle: "Luis-Fernando-Molina", + blockchainAccount: "asdasdasdasdasdasdasdaasdasdasdasdasdasdsd", + ranking: 0, + host: "localhost", + port: global.env.NETWORK_WEB_SOCKETS_INTERFACE_PORT + } + + socketClient = new SA.nodeModules.ws('ws://' + selectedNetworkNode.host + ':' + selectedNetworkNode.port) //networkInterface = SA.projects.socialTrading.modules.networkInterface.newSocialTradingModulesNetworkInterface() setUpWebsocketClient() @@ -30,15 +49,137 @@ exports.newNetworkModulesWebSocketsClient = function newNetworkModulesWebSockets let handshakeDone = false socketClient.onopen = () => { onConnection() } - socketClient.onmessage = socketMessage => { onMenssage(socketMessage) } socketClient.onerror = err => { onError(err) } function onConnection() { - /* - Send Handshake Message - */ - socketClient.send('Handshake Message') - console.log('Handshake Message Sent') + + handshakeProcedure() + + function handshakeProcedure() { + + let callerTimestamp + + stepOneRequest() + + function stepOneRequest() { + /* + Send Handshake Message Step One: + + At Step One we will just say hello, identify ourselves + as a Network Client, and send the Network Node our + User Profile Handle. + + This Handle will be signed by the Network Node to prove + it's ouwn identity, and later we will sign it's own handle + to prove ours. + */ + socketClient.onmessage = socketMessage => { stepOneResponse(socketMessage) } + + callerTimestamp = (new Date()).valueOf() + + let message = { + messageType: 'Handshake', + callerRole: 'Network Client', + userProfileHandle: DK.NETWORK_CLIENT_USER_PROFILE_HANDLE, + callerTimestamp: callerTimestamp, + step: 'One' + } + socketClient.send(message) + } + + function stepOneResponse(socketMessage) { + let response = JSON.parse(socketMessage.data) + + if (response.result !== 'Ok') { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> response.message = ' + response.message) + return + } + + let signature = JSON.parse(response.signature) + called.blockchainAccount = web3.eth.accounts.recover(signature) + /* + We will check that the signature received produces a valid Blockchain Account. + */ + if (called.blockchainAccount === undefined) { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> Signature does not produce a valid Blockchain Account.') + return + } + /* + We will check that the blockchain account taken from the signature matches + the one we have on record for the user profile of the Network Node we are calling. + */ + if (called.blockchainAccount !== selectedNetworkNode.blockchainAccount) { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node called does not have the expected Profile Handle.') + return + } + + let signedMessage = JSON.parse(signature.message) + + /* + We will check that the Network Node that responded has the same User Profile Handle + that we have on record, otherwise something is wrong and we should not proceed. + */ + if (signedMessage.calledProfileHandle !== selectedNetworkNode.userProfileHandle) { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node called does not have the expected Profile Handle.') + return + } + /* + We will check that the profile handle we sent to the Network Node, is returned at the + signed message, to avoid man in the middle attackts. + */ + if (signedMessage.callerProfileHandle !== DK.NETWORK_CLIENT_USER_PROFILE_HANDLE) { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node callerProfileHandle does not match my own userProfileHandle.') + return + } + /* + We will also check that the callerTimestamp we sent to the Network Node, is returned at the + signed message, also to avoid man in the middle attackts. + */ + if (signedMessage.callerTimestamp !== callerTimestamp) { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> The Network Node callerTimestamp does not match my own callerTimestamp.') + return + } + + /* + All validations passed, it seems we can continue with Step Two. + */ + stepTwoRequest(signedMessage) + } + + function stepTwoRequest(signedMessage) { + /* + Send Handshake Message Step Two: + + Here we will sign a message with the Network Node profile + handle and timestamp to prove our own identity. + */ + socketClient.onmessage = socketMessage => { stepTwoResponse(socketMessage) } + + let signature = web3.eth.accounts.sign(JSON.stringify(signedMessage), DK.NETWORK_CLIENT_USER_PROFILE_PRIVATE_KEY) + + let message = { + messageType: 'Handshake', + signature: JSON.stringify(signature), + step: 'Two' + } + socketClient.send(message) + } + + function stepTwoResponse(socketMessage) { + let response = JSON.parse(socketMessage.data) + + if (response.result !== 'Ok') { + console.log('[ERROR] Web Sockets Client -> stepOneResponse -> response.message = ' + response.message) + return + } + /* + This was the end of the Handshake producere. We are connected to the + Network Node and from now on, all response messages will be received + at this following function. + */ + socketClient.onmessage = socketMessage => { onMenssage(socketMessage) } + } + } } async function onMenssage(socketMessage) { @@ -53,10 +194,10 @@ exports.newNetworkModulesWebSocketsClient = function newNetworkModulesWebSockets handshakeDone = true return } else { - console.log('[ERROR] Web Sockets Client -> onMenssage -> handshakeDone -> message.response = ' + response.message) + console.log('[ERROR] Web Sockets Client -> onMenssage -> handshakeDone -> response.message = ' + response.message) return } - } + } callbackFunction.receiveResponse(message) callbackFunction = undefined diff --git a/Projects/Social-Trading/NT/Modules/Queries/Events.js b/Projects/Social-Trading/NT/Modules/Queries/Events.js index 2cee66514..a68d7215b 100644 --- a/Projects/Social-Trading/NT/Modules/Queries/Events.js +++ b/Projects/Social-Trading/NT/Modules/Queries/Events.js @@ -105,7 +105,6 @@ exports.newSocialTradingModulesQueriesEvents = function newSocialTradingModulesQ } } } - /* Test #2 : The Emitter or Traget profile must be at the Following map of the Context Profile. */ diff --git a/runDesktop.js b/runDesktop.js index 96c4c24c5..425712d23 100644 --- a/runDesktop.js +++ b/runDesktop.js @@ -45,6 +45,9 @@ SA.nodeModules = { ws: require('ws') } +DK.NETWORK_CLIENT_USER_PROFILE_HANDLE = "Julian-Molina" // TODO: remove this. +DK.NETWORK_CLIENT_USER_PROFILE_PRIVATE_KEY = "dasdasdjhakdhakhdkalshdkashdka" // TODO: remove this. + DK.app = require('./Desktop/DesktopApp.js').newDesktopApp() DK.app.run() diff --git a/runNetwork.js b/runNetwork.js index 9aae83d5e..e0c5ed174 100644 --- a/runNetwork.js +++ b/runNetwork.js @@ -46,6 +46,9 @@ SA.nodeModules = { ws: require('ws') } +NT.NETWORK_NODE_USER_PROFILE_HANDLE = "Luis-Fernando-Molina" // TODO: remove this. +NT.NETWORK_NODE_USER_PROFILE_PRIVATE_KEY = "blablablablablablablablablablablablablablablablabla" // TODO: remove this. + NT.app = require('./Network/NetwokNode.js').newNetworkNode() NT.app.run()