Files
Jędrzej Stuczyński d9d549fd0f Feature/reply surbs (#299)
* Changed identity keypair to use ed25519

* Encryption key is now x25519 based + compatibiltiy with sphinx

* Pathing and import fixes

* Moved all asymmetric keys to sub-module in crypto

* Extracted aes to separate module

* kdf module in crypto

* Ability to perform diffie hellman on encryption keys

* ecdsa on identity keys

* Extremely rough and incomplete registration handshake

* Authentication primitives

* Creating new random authenticationIV

* Wrapper type for the derived shared key

* Removed AuthToken in favour of using SharedKey for authentication

* Gateway identity keys

* Registration handshake without error mapping

* Gateway address in client config

* Added extra key for gateway presence

* Updated pemstore to work on borrows instead

* Gateway client trying to perform the handshake

* Gateway changes to allow for handshake and shared key

* Debug trait on sharedkey

* native client using updated gateway client

* Slightly updated gateway API

* Minor cleanup

* Fixed pemstore to correctly save multiple keypairs

* Gateway actually deriving shared key during handshake

* Gateway sending correct mid-handshake message

* Missing quotation mark in client config template

* Fixed template for correct shared key serialization

* Fixed gateway authentication

* Fixed tests

* Using correct gateway key when converting to sphinx node

* "get_all_clients" takes them from gateways as opposed to providers now

* cargo fmt

* Renamed pemstore methods

* Unused import

* Encryption of forward requests between client and gateway

* Updated sphinx dependency to use public revision

* Sending 'error' on handshake processing error

* Removed some dead code

* Dead code I forgot to remove before

* Extracted AckAes128Key into a struct

* Slight pemstore revamp allowing for symmetric key store

* ibid.

* PemStorableKey for SharedKey

* Introduced single location responsible for key management for client

* WIP

* Sphinx version update

* Stop using NodeAddressBytes for two distinct and confusing purposes

* Abstracting away SocketAddr from sphinx forwarding

* Passing the bool for reply surbs

* Attack plan for replies + encryption

* Comment + removed variable binding

* ReplySURB usage

* Topology import in nymsphinx

* Sphinx version update

* Changed 'Recipient' to contain client's encryption key

* Message preparation taking shape!

* reply surb also containing the encryption key

* Very initial message receiver

* Sphinx version update

* A possibly working way of receiving surbs

* Fixed incorrect field name in client config template

* camel casing all request arguments

* Renamed and moved `MessageMode` to more appropriate file

* Restored reconstruction tests

* Removed dead code from chunking

* Made rust examples compilable

* reply SURB key storage

* Replies as an InputMessage

* Forgotten commented code

* No retransmission processing for cover or replies

* Received reply processing

* Renamed client pathfinder to something more appropriate

* Made HasherOutputSize public

* Added key store path to config

* Reply surb attaching key digest when used

* Changes due to previous renaming

* Removed comment

* Fixed insert_encryption_key

* Assigning initial value of key store path

* Computing key digest with correct algorithm

* Initial and presumably temporary request serialization

* hacky way of introducing 'FragmentIdentifier' for replies

* Moved responsibility of reply encryption, padding, etc, to message preparer

* Optional recipient in try_get_valid_topology_ref

* Handling new reply surbs with acks and padding

* Updated go and python examples to include replies in text and binary cases

* Updated rust examples + binaryserverresponse

* Helpers in rust examples

* And updated JS example

* Moved shared key generation function to crypto crate

* Cover traffic encryption!

* hmac computation in crypto

* Updated aes imports due to new dependencies

* hkdf made more generic

* crypto cleanup + algorithms in params

* Clippy cleanup pass

* Generating encryption+mac shared keys between client and gateway

* MACs attached to forward requests to gateway

* Gateway messages encrypted and mac'd

* Lowered logging level

* compiler warning cleanup

* Some minor cleanup

* Generic stream cipher

* Generic shared key derivation + algorithm definitions

* Project-wide AES clean-up

* Comment fix

* Removed commented imports

* Updated comments

* Fixed topology test fixture
2020-08-07 18:16:54 +01:00

174 lines
5.7 KiB
JavaScript

var ourAddress;
var connection;
// Please note that this javascript is extremely bad, it's only purpose is to show some basic API calls, not how
// a proper application should have been written.
async function main() {
var port = '1977' // client websocket listens on 1977 by default, change if yours is different
var localClientUrl = "ws://127.0.0.1:" + port;
// Set up and handle websocket connection to our desktop client.
connection = await connectWebsocket(localClientUrl).then(function (c) {
return c;
}).catch(function (err) {
display("Websocket connection error. Is the client running with <pre>--connection-type WebSocket</pre> on port " + port + "?");
})
connection.onmessage = function (e) {
handleResponse(e);
};
sendSelfAddressRequest();
// Set up the send button
const sendButton = document.querySelector('#send-button');
sendButton.onclick = function () {
sendMessageToMixnet();
}
}
// Handle any messages that come back down the websocket.
function handleResponse(resp) {
// hacky workaround for receiving pushed 'text' messages,
// basically we can either receive proper server responses, i.e. 'error', 'send', 'selfAddress'
// or actual messages, without any framing, so they do not have 'type' field
try {
let response = JSON.parse(resp.data);
if (response.type == "error") {
displayJsonResponseWithoutReply("Server responded with error: " + response.message);
} else if (response.type == "selfAddress") {
displayJsonResponseWithoutReply(response);
ourAddress = response.address;
display("Our address is: " + ourAddress + ", we will now send messages to ourself.");
} else if (response.type == "received") {
handleReceivedTextMessage(response)
}
} catch (_) {
displayJsonResponseWithoutReply(resp.data)
}
}
function handleReceivedTextMessage(message) {
console.log("received a message!")
const text = message.message
const replySurb = message.replySurb
if (replySurb != null) {
displayJsonResponseWithReply(text, replySurb)
} else {
displayJsonResponseWithoutReply(text)
}
}
// Send a message to the mixnet.
function sendMessageToMixnet() {
const sendText = document.getElementById("sendtext").value;
const surbCheckbox = document.querySelector('#with-surb');
const attachReplySURB = surbCheckbox.checked;
const message = {
type: "send",
message: sendText,
recipient: ourAddress,
withReplySurb: attachReplySURB,
}
displayJsonResponseWithoutReply(message);
connection.send(JSON.stringify(message));
}
function sendReplyMessageToMixnet(messageContent, replySurb) {
const message = {
type: "reply",
message: messageContent,
replySurb: replySurb,
}
displayJsonResponseWithoutReply(message);
connection.send(JSON.stringify(message));
}
// Send a message to the mixnet client, asking what our own address is.
// In this simplistic demo, we'll just use our own address to send ourselves messages.
//
// In a real application, you might want to ensure that somebody else got your
// address so that they could send messages to you.
function sendSelfAddressRequest() {
var selfAddress = {
type: "selfAddress"
}
displayJsonSend(selfAddress);
connection.send(JSON.stringify(selfAddress));
}
function display(message) {
document.getElementById("output").innerHTML += "<p>" + message + "</p >";
}
function displayJsonSend(message) {
let sendDiv = document.createElement("div")
let paragraph = document.createElement("p")
paragraph.setAttribute('style', 'color: blue')
let paragraphContent = document.createTextNode("sent >>> " + JSON.stringify(message))
paragraph.appendChild(paragraphContent)
sendDiv.appendChild(paragraph)
document.getElementById("output").appendChild(sendDiv)
}
function displayJsonResponseWithoutReply(message) {
let receivedDiv = document.createElement("div")
let paragraph = document.createElement("p")
paragraph.setAttribute('style', 'color: green')
let paragraphContent = document.createTextNode("received >>> " + JSON.stringify(message) + "(NO REPLY AVAILABLE)")
paragraph.appendChild(paragraphContent)
receivedDiv.appendChild(paragraph)
document.getElementById("output").appendChild(receivedDiv)
}
function displayJsonResponseWithReply(message, replySurb) {
let replyBox = document.createElement("input")
replyBox.setAttribute('type', 'text');
replyBox.setAttribute('value', 'type your anonymous reply here!');
replyBox.setAttribute('size', 50);
let sendButton = document.createElement("button")
let buttonText = document.createTextNode("Send")
sendButton.appendChild(buttonText)
sendButton.onclick = () => {
sendReplyMessageToMixnet(replyBox.value, replySurb)
}
let receivedDiv = document.createElement("div")
let paragraph = document.createElement("p")
paragraph.setAttribute('style', 'color: green')
let paragraphContent = document.createTextNode("received >>> " + JSON.stringify(message) + "(HERE BE SURB)")
paragraph.appendChild(paragraphContent)
receivedDiv.appendChild(paragraph)
receivedDiv.appendChild(replyBox)
receivedDiv.appendChild(sendButton)
document.getElementById("output").appendChild(receivedDiv)
}
// Connect to a websocket.
function connectWebsocket(url) {
return new Promise(function (resolve, reject) {
var server = new WebSocket(url);
server.onopen = function () {
resolve(server);
};
server.onerror = function (err) {
reject(err);
};
});
}
// Start it!
main();