Skip to content

Commit

Permalink
feat: split WASM (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
rentallect authored Jul 9, 2023
1 parent 4653e5e commit c21abcd
Show file tree
Hide file tree
Showing 14 changed files with 572 additions and 446 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"typescript": "^4.6.4"
},
"dependencies": {
"@openziti/libcrypto-js": "^0.14.1",
"@openziti/libcrypto-js": "^0.15.0",
"@openziti/ziti-browzer-edge-client": "^0.6.0",
"asn1js": "^2.4.0",
"assert": "^2.0.0",
Expand Down
110 changes: 55 additions & 55 deletions src/channel/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,6 @@ class ZitiChannel {

if (isEqual(this._callerId, "ws:")) {

// this._zitiContext.ssl_CTX_new();
// // this._zitiContext.bio_new_ssl_connect();
// // this._zitiContext.bio_set_conn_hostname('www.google.com:443');
// // this._zitiContext.bio_do_connect();

// this._zitiContext.ssl_new();
// this._zitiContext.ssl_set_fd( this.id );
// this._zitiContext.ssl_connect();

this._tlsConn = new ZitiWASMTLSConnection({
zitiContext: this._zitiContext,
ws: this._zws,
Expand All @@ -259,7 +250,7 @@ class ZitiChannel {

this._zitiContext.logger.debug('initiating TLS handshake');

this._tlsConn.handshake();
await this._tlsConn.handshake();

await this.awaitTLSHandshakeComplete();

Expand Down Expand Up @@ -1045,6 +1036,8 @@ class ZitiChannel {

const release = await this._mutex.acquire();

let zeroByteData = false;

/**
* First Data msg for a new connection needs special handling
*/
Expand Down Expand Up @@ -1080,6 +1073,8 @@ class ZitiChannel {

if ( isEqual(contentType, ZitiEdgeProtocol.content_type.Data) && isEqual(bodyLength, 0) ) {

zeroByteData = true;

// let result = await this._messageGetBytesHeader(data, ZitiEdgeProtocol.header_id.SeqHeader);
// replyForView = new Int32Array(result.data, 0, 1);
// responseSequence = replyForView[0];
Expand All @@ -1104,74 +1099,79 @@ class ZitiChannel {
let connId = await this._messageGetConnId(data);
throwIf(isUndefined(connId), formatMessage('Cannot find ConnId header', { } ) );
conn = this._connections._getConnection(connId);
throwIf(isUndefined(conn), formatMessage('Conn not found. Seeking connId { actual }', { actual: connId}) );
if (!zeroByteData) {
throwIf(isUndefined(conn), formatMessage('Conn not found. Seeking connId { actual }', { actual: connId}) );
}
}

/**
* Data msgs might need to be decrypted before passing along
*/
if (contentType == ZitiEdgeProtocol.content_type.Data) {

if (bodyLength > 0) {
if (!isUndefined(conn)) {

if (conn.encrypted && conn.cryptoEstablishComplete) { // if connected to a service that has 'encryptionRequired'
if (bodyLength > 0) {

let unencrypted_data = sodium.crypto_secretstream_xchacha20poly1305_pull(conn.crypt_i, bodyView);
if (conn.encrypted && conn.cryptoEstablishComplete) { // if connected to a service that has 'encryptionRequired'

if (!unencrypted_data) {
this._zitiContext.logger.error("crypto_secretstream_xchacha20poly1305_pull failed. bodyLength[%d]", bodyLength);
}
let unencrypted_data = sodium.crypto_secretstream_xchacha20poly1305_pull(conn.crypt_i, bodyView);

try {
let [m1, tag1] = [sodium.to_string(unencrypted_data.message), unencrypted_data.tag];
let len = m1.length;
if (len > 2000) {
len = 2000;
if (!unencrypted_data) {
this._zitiContext.logger.error("crypto_secretstream_xchacha20poly1305_pull failed. bodyLength[%d]", bodyLength);
}
// this._zitiContext.logger.trace("recv <- unencrypted_data (first 2000): %s", m1.substring(0, len));

//
// let dbgStr = m1.substring(0, len);
// this._zitiContext.logger.trace("recv <- data (first 2000): %s", dbgStr);
try {
let [m1, tag1] = [sodium.to_string(unencrypted_data.message), unencrypted_data.tag];
let len = m1.length;
if (len > 2000) {
len = 2000;
}
// this._zitiContext.logger.trace("recv <- unencrypted_data (first 2000): %s", m1.substring(0, len));

} catch (e) { }
//
// let dbgStr = m1.substring(0, len);
// this._zitiContext.logger.trace("recv <- data (first 2000): %s", dbgStr);

bodyView = unencrypted_data.message;
} else {
/* debug...
let len = bodyView.length;
if (len > 2000) {
len = 2000;
}
let dbgStr = String.fromCharCode.apply(null, bodyView).substring(0, len);
this._zitiContext.logger.debug("recv <- data (first 2000): %s", dbgStr);
*/
} catch (e) { }

bodyView = unencrypted_data.message;
} else {
/* debug...
let len = bodyView.length;
if (len > 2000) {
len = 2000;
}
let dbgStr = String.fromCharCode.apply(null, bodyView).substring(0, len);
this._zitiContext.logger.debug("recv <- data (first 2000): %s", dbgStr);
*/

//temp debugging
// if (dbgStr.includes("var openMe = (window.parent")) {
//temp debugging
// if (dbgStr.includes("var openMe = (window.parent")) {

// let str = String.fromCharCode.apply(null, bodyView).substring(0, bodyView.length);
// let str = String.fromCharCode.apply(null, bodyView).substring(0, bodyView.length);

// // str = str.replace('var openMe = (window.parent', 'debugger; var openMe = (window.parent');
// // str = str.replace('var openMe = (window.parent', 'debugger; var openMe = (window.parent');

// if (str.indexOf( '/api/extplugins/config' ) !== -1) {
// debugger
// }
// if (str.indexOf( '/api/extplugins/config' ) !== -1) {
// debugger
// }

// this._zitiContext.logger.debug("============== DEBUG INJECT: %s", str);
// this._zitiContext.logger.debug("============== DEBUG INJECT: %s", str);

// bodyView = new TextEncoder("utf-8").encode(str);

// }
// bodyView = new TextEncoder("utf-8").encode(str);
// }

}
}
}

//
let dataCallback = conn.dataCallback;
if (!isUndefined(dataCallback)) {
this._zitiContext.logger.debug("recv <- conn[%o] contentType[%o] seq[%o] passing body to dataCallback", conn.id, contentType, sequenceView[0]);
dataCallback(conn, bodyView);
//
let dataCallback = conn.dataCallback;
if (!isUndefined(dataCallback)) {
this._zitiContext.logger.debug("recv <- conn[%o] contentType[%o] seq[%o] passing body to dataCallback", conn.id, contentType, sequenceView[0]);
dataCallback(conn, bodyView);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/channel/tls-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import { v4 as uuidv4 } from 'uuid';
*/
async pullKeyPair() {

this._privateKeyPEM = this._zitiContext.privateKeyPEM;
this._privateKeyPEM = this._zitiContext.get_privateKeyPEM();

this._certPEM = await this._zitiContext.getCertPEM();

Expand Down
99 changes: 58 additions & 41 deletions src/channel/wasm-tls-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
this._id = this._ch.id;
this._datacb = this._options.datacb;

this._connected_cb = null;
// this._connected_cb = null;
this._connected = false;

this._read_cb = null;
Expand Down Expand Up @@ -98,7 +98,7 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
*/
async pullKeyPair() {

this._privateKeyPEM = this._zitiContext.privateKeyPEM;
this._privateKeyPEM = this._zitiContext.get_privateKeyPEM();

this._certPEM = await this._zitiContext.getCertPEM();

Expand All @@ -123,17 +123,21 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
*/
async create() {

this._sslContext = await this._zitiContext.ssl_CTX_new();
this._wasmInstance = await this._zitiContext.getInstance_OuterWASM();

this._BIO = this._zitiContext.bio_new_ssl_connect(this._sslContext);
this._sslContext = await this._zitiContext.ssl_CTX_new( this._wasmInstance );

this._SSL = this._zitiContext.bio_get_ssl(this._BIO);
this._zitiContext.logger.trace('ZitiWASMTLSConnection.create() _zitiContext.ssl_CTX_new() returned [%o]', this._sslContext);

this._BIO = this._zitiContext.bio_new_ssl_connect(this._wasmInstance, this._sslContext);

this._SSL = this._zitiContext.bio_get_ssl(this._wasmInstance, this._BIO);


// Tie the WASM-based SSL object back to this ZitiWASMTLSConnection so that later when
// the low-level WASM code does fd-level i/o, our WAS-JS will intercept it, and
// the low-level WASM code does fd-level i/o, our WASM-JS will intercept it, and
// interface with this connection, so we can route traffic over the WebSocket to the ER
this._zitiContext.ssl_set_fd( this._SSL, this._ch.id );
this._zitiContext.ssl_set_fd( this._wasmInstance, this._SSL, this._ch.id );

}

Expand Down Expand Up @@ -174,46 +178,59 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
/**
*
*/
handshake_cb(self, rc) {
self._zitiContext.logger.trace('ZitiWASMTLSConnection.handshake_cb(): entered rc=%d ', rc );

// Let's delay a smidge, and allow the WASM mTLS ciphersuite-exchange to complete,
// before we turn loose any writes to the connection
setTimeout((tlsConn, rc) => {
self._zitiContext.logger.trace("ZitiWASMTLSConnection.handshake_cb(): after timeout");
self._connected = true;
}, 500, self, rc)
}
// handshake_cb(self, rc) {
// self._zitiContext.logger.trace('ZitiWASMTLSConnection.handshake_cb(): entered rc=%d ', rc );

// // Let's delay a smidge, and allow the WASM mTLS ciphersuite-exchange to complete,
// // before we turn loose any writes to the connection
// setTimeout((tlsConn, rc) => {
// self._zitiContext.logger.trace("ZitiWASMTLSConnection.handshake_cb(): after timeout");
// self._connected = true;
// }, 500, self, rc)
// }

/**
*
*/
handshake() {
async handshake() {

// Make sure WASM knows where to callback to once handshake is complete
this._connected_cb = this.handshake_cb;


// this._connected_cb = this.handshake_cb;

this._zitiContext.logger.trace('ZitiWASMTLSConnection.handshake(): fd[%d] calling ssl_do_handshake()', this.wasmFD );
let result = this._zitiContext.ssl_do_handshake( this._SSL );
let result = this._zitiContext.ssl_do_handshake( this._wasmInstance, this._SSL );
this._zitiContext.logger.trace('ZitiWASMTLSConnection.handshake(): fd[%d] back from ssl_do_handshake() for %o: result=%d (now awaiting cb)', this.wasmFD, this._id, result );
}

ssl_get_verify_result() {
let result = this._zitiContext.ssl_get_verify_result( this._SSL );
this._zitiContext.logger.trace('ZitiWASMTLSConnection.ssl_get_verify_result(): for: %o: result: ', this._id, result );
return result;
}
// ssl_get_verify_result() {
// let result = this._zitiContext.ssl_get_verify_result( this._SSL );
// this._zitiContext.logger.trace('ZitiWASMTLSConnection.ssl_get_verify_result(): for: %o: result: ', this._id, result );
// return result;
// }

/**
*
*/
get connected() {
return this._connected;
}
set connected(state) {
this._connected = state;

// If we already did the work, and are fully connected, take a quick exit
if (this._connected) return true;

// Ask the SSL if its handshake has completed yet
let _connected = this._zitiContext.ssl_is_init_finished(this._wasmInstance, this._SSL);

// If SSL indicates handshake has completed, let's delay a smidge, and allow the WASM mTLS ciphersuite-exchange to complete,
// before we turn loose any writes to the connection
if (_connected) {

setTimeout((self) => {
self._zitiContext.logger.trace("ZitiWASMTLSConnection.connected(): after timeout");
self._connected = true;
}, 100, this);

}

return this._connected
}


Expand Down Expand Up @@ -246,16 +263,16 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
async process(arrayBuffer) {
this._zitiContext.logger.trace('ZitiWASMTLSConnection.process[%d] encrypted data from the ER arrived <--- [%o]', this.wasmFD, arrayBuffer);

await this._zitiContext.tls_enqueue(this.wasmFD, arrayBuffer); // enqueue the encrypted data (place it in WASM memory)
await this._zitiContext.tls_enqueue(this._wasmInstance, this.wasmFD, arrayBuffer); // enqueue the encrypted data (place it in WASM memory)

// If the TLS handshake has completed, we'll need to do TLS-decrypt of the data,
// and then propagate it to the Promise that is waiting for it.
if (this._connected) {
if (this.connected) {

// Make sure WASM knows where to callback when decrypted data is ready
// this._read_cb = this.read_cb;

let decryptedData = this._zitiContext.tls_read(this._SSL); // TLS-decrypt some data from the queue (bring back from WASM memory into JS memory)
let decryptedData = this._zitiContext.tls_read(this._wasmInstance, this._SSL); // TLS-decrypt some data from the queue (bring back from WASM memory into JS memory)

this._zitiContext.logger.trace('ZitiWASMTLSConnection.process[%d]: clear data from the ER is ready <--- len[%d]', this.wasmFD, decryptedData.byteLength);
this._datacb(this._ch, decryptedData.buffer); // propagate clear data to the waiting Promise
Expand All @@ -277,7 +294,7 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
*
* @param {*} wireData (not TLS-encrypted yet)
*/
tls_write(wireData, conn) {
async tls_write(wireData, conn) {
this._zitiContext.logger.trace(`ZitiWASMTLSConnection.tls_write unencrypted data is ready to be sent to the ER ---> len[${wireData.byteLength}]`);

// If connection has an innerTLSsocket, and it has completed its TLS handshake
Expand All @@ -287,26 +304,26 @@ import {Mutex, withTimeout, Semaphore} from 'async-mutex';
// ...then pass it to the Channel
this._zitiContext.logger.trace(`ZitiWASMTLSConnection.tls_write sending encrypted wireData from innerTLSSocket to _zitiContext.tls_write`);
conn._socket.innerTLSSocket._sendingEncryptedData = false; // reset now that we're sending to outer socket
this._zitiContext.tls_write(this._SSL, wireData);
this._zitiContext.tls_write(this._wasmInstance, this._SSL, wireData);
} else {
// ...otherwise pass it to the innerTLSsocket so it can do the necessary TLS encryption according to the handshake that was completed with
// the connected service (i.e. web server listening on TLS)
this._zitiContext.logger.trace(`ZitiWASMTLSConnection.tls_write sending un-encrypted wireData to innerTLSSocket.tls_write`);
conn._socket.innerTLSSocket.tls_write(wireData);
}
} else {
this._zitiContext.tls_write(this._SSL, wireData);
this._zitiContext.tls_write(this._wasmInstance, this._SSL, wireData);
}
}

/**
*
* @param {*} wireData (TLS-encrypted at the innerTLS level, but not TLS-encrypted at mTLS level yet)
*/
tls_write_outer(wireData) {
this._zitiContext.logger.trace('ZitiWASMTLSConnection.tls_write_outer[%o] unencrypted data is ready to be sent to the ER ---> [%o]', this._uuid, wireData);
this._zitiContext.tls_write(this._SSL, wireData.buffer);
}
// tls_write_outer(wireData) {
// this._zitiContext.logger.trace('ZitiWASMTLSConnection.tls_write_outer[%o] unencrypted data is ready to be sent to the ER ---> [%o]', this._uuid, wireData);
// this._zitiContext.tls_write(this._SSL, wireData.buffer);
// }

/**
*
Expand Down
Loading

0 comments on commit c21abcd

Please sign in to comment.