Skip to content

Commit

Permalink
Compatibility with JS module system.
Browse files Browse the repository at this point in the history
This PR makes libsignal-protocol-javascript compatible with JS's module system.
The test suite now requires the library, bundling it into a single dependency.
We also add an integration test.

For backward compatibility, we still build dist/libsignal.js, and attach
the library to window.libsignal.

A few points remain, for the contributor's consideration:

- This package will not work in node due to a dependency on WebCrypto API. We could look into polyfills, or allow the caller pass in their own primatives (though I do NOT think this is a good idea).

- A few things are listed as dependencies in package.json. However, we are bundling those into a file anyway, so they are really dev dependencies (npm will not need to install them).

This is a combination of 21 commits. Each commit's comments listed below.

browserifying some test bundle

Starting to bundle the tests.

There is now a file test/main.js that requires and executes all the tests.

Each test file is now responsible for requiring its own dependencies.

test/index.html will eventually do nothing more than require chai/mocha/etc test lib bundle (test/test.js, which is built in the gruntfile concat task), and the test suite bundle (build/test_main.js). Perhaps we should move this bundle to test to be like build.

Crypto.js tested + module-ified

Curve.js still relies on some stuff in Internal that we will have to track down eventually.

Continuing bundle-ifiabilty

Stopping me now is the way protos/WhsiperTextProtocol.proto is built
I assume this is an emscripten setting in grunt.
What I intend to do is remove all Internal = Internal | {} garbage
and replace with a single module.exports at the end.
This way, the native stuff with expose itself as a function/object.

SessionCipherTest passing

This one required fiddling with the way protos are built

I simplified stuff I think without breaking anything - just a one line change after some thinking. It seems that Whisper devs intended to add more protobufs eventually. I'm not sure if that's still in the plans, but they should be able to with some hacks. However, a more module-friendly dynamic loading solution may be easier to reason aout. Not bothering to write that for now.

KeyHelper and NumericFingerprint pass

Nothing fancy here, just requiring.

SessionBuilderTest passing

Also added a few untracked files.
After all tests pass, I'm realizing I'll need to go back and make the build workflow sane again for testing. The browserify step is really *only* for the tests.

Passing store tests

The store tests come in a set of 5.
Each gets initialized as a function,
and SignalProtocolStore_test.js ties them all together.

Where before the methods were being called from out of scope,
here we require each store test in test/main.js, then pass all of these functions into test/SiganlProtocolStore_test.js, which now exposes a higher-order function.

Passing all tests!

Woohoo!

Now it's time to deal with those final places where I'm "cheating." In test/index.html, there are three things from ../build/ (and one from ../src/, a worker) that are still being added to the HTML the old fashioned way, through script tags.

The first order of business is that stuff in build/. Figuring out how it's written to JS, and how to module-ify it (and where to include it later) will surely take some time.

Moving toward modular emscripten + worker

I finally figured out how to require emscripten code from browserify!
http://insertafter.com/en/blog/native-node-module.html?

Now that I can require curve stuff instead of appending,
i'ts time to get the webworkers into browserify too.
Of course, substack has written something for this:
https://github.com/substack/webworkify
and it looks like that'll do what I need
....but, I'll need to get it working with the existing manager, first
That's a mindfuck in itself because of all the zig-zagging references, even within the small file.

What I'll need to do first is make sure 1 worker can get launched and do its job.
After that, we can tidy up the references in that file,
then make sure all references to startWorker fall in line.

Fixed typo

The crypto all seems to work now.
Not sure what the webworker is for - will return to it once I figure that out.

Amazingly, the only thing left is a regular old npm module.
Why the unexpected behavior? Is Long the issue?
How can I force Long in browserify, if so?

Fixed webworkers

WebWorkers now seem to work with browserify.

Bundled tests lib in build/

They were getting written to test/ before - confusing, no computer-generated stuff in test/
That should be human-written source only.
Now everything in test/index.html points to something in build/

Working!

All bundled up!

The trick was just requiring the bundled file apparently (?)

Only things left to do are:
- Make sure blanket runs at end-of-tests
- Turn linting back on
- Write up changes in README

Added blanket; tests fail

After adding blanket back, tests are suddenly failing!
Not sure why.

Removing blanket again

Indeed, removing blanket fixes the failures.
Oh well, will leave it out for now.

All tests running

Preserving legacy builds

We still have a prebundled JS file for people who want to include in HTML.

Passing grunt jscs

Updated jshint

Linting back on
  • Loading branch information
elsehow committed Dec 12, 2016
1 parent c4d1237 commit e4debbf
Show file tree
Hide file tree
Showing 47 changed files with 69,375 additions and 103,651 deletions.
7 changes: 5 additions & 2 deletions .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"sub" : false,
"supernew" : false,
"validthis" : false,
"browser" : false,
"browser" : true,
"browserify" : false,
"couch" : false,
"devel" : false,
Expand All @@ -67,5 +67,8 @@
"worker" : false,
"wsh" : false,
"yui" : false,
"globals" : {}
"node" : true,
"globals" : {
"Promise" : true
}
}
73 changes: 23 additions & 50 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,35 @@ module.exports = function(grunt) {

grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
browserify: {
legacy: {
src: 'src/main_window.js',
dest: 'dist/libsignal.js'
},
test: {
src: 'test/main.js',
dest: 'build/test_main.js'
}
},
concat: {
components: {
src: [
'node_modules/long/dist/long.js',
'node_modules/bytebuffer/dist/ByteBufferAB.js',
'node_modules/protobufjs/dist/protobuf.js'
],
dest: 'build/components_concat.js',
dest: 'build/dcodeIO.js'
},
curve25519: {
src: [
'build/curve25519_compiled.js',
'src/curve25519_wrapper.js',
],
dest: 'build/curve25519_concat.js'
dest: 'build/curve25519_concat.js',
// Append this to the build for browser require compatibility
// via https://github.com/nfroidure/ttf2woff2/blob/master/jssrc/post.js
options: {
footer: "module.exports = Module;\nModule.inspect = function() { return '[Module]'; };"
}
},
protos: {
src: [
Expand Down Expand Up @@ -48,43 +62,6 @@ module.exports = function(grunt) {
dest: 'build/protobufs_concat.js'
},

worker: {
src: [
'build/curve25519_concat.js',
'src/curve25519_worker.js',
],
dest: 'dist/libsignal-protocol-worker.js',
options: {
banner: ';(function(){\nvar Internal = {};\nvar libsignal = {};\n',
footer: '\n})();'
}

},
libsignalprotocol: {
src: [
'build/curve25519_concat.js',
'src/curve25519_worker_manager.js',
'build/components_concat.js',

'src/Curve.js',
'src/crypto.js',
'src/helpers.js',
'src/KeyHelper.js',
'build/protobufs_concat.js',
'src/SessionRecord.js',
'src/SignalProtocolAddress.js',
'src/SessionBuilder.js',
'src/SessionCipher.js',
'src/SessionLock.js',
'src/NumericFingerprint.js'
],
dest: 'dist/libsignal-protocol.js',
options: {
banner: ';(function(){\nvar Internal = {};\nwindow.libsignal = {};\n',
footer: '\n})();'
}

},
test: {
src: [
'node_modules/mocha/mocha.js',
Expand All @@ -93,10 +70,7 @@ module.exports = function(grunt) {
'node_modules/blanket/dist/mocha/blanket_mocha.js',
'test/_test.js'
],
dest: 'test/test.js',
options: {
banner: 'var Internal = {};\nwindow.libsignal = {};\n'
}
dest: 'build/test_lib.js'
}
},
compile: {
Expand Down Expand Up @@ -138,10 +112,10 @@ module.exports = function(grunt) {
files: ['<%= jshint.files %>', '.jshintrc'],
tasks: ['jshint']
},
worker: {
files: ['<%= concat.worker.src %>'],
tasks: ['concat:worker']
},
// worker: {
// files: ['<%= concat.worker.src %>'],
// tasks: ['concat:worker']
// },
libsignalprotocol: {
files: ['<%= concat.libsignalprotocol.src %>'],
tasks: ['concat:libsignalprotocol']
Expand Down Expand Up @@ -224,6 +198,5 @@ module.exports = function(grunt) {
grunt.registerTask('dev', ['connect', 'watch']);
grunt.registerTask('test', ['jshint', 'jscs', 'connect', 'saucelabs-mocha']);
grunt.registerTask('default', ['concat']);
grunt.registerTask('build', ['compile', 'concat']);

grunt.registerTask('build', ['compile', 'concat', 'browserify']);
};
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,23 @@ types/interfaces, which are available in most modern browsers.

## Usage

Include `dist/libsignal-protocol.js` in your webpage.
There are two ways to use this package.

You can require with your front-end bundler of choice (e.g. browserify, webpack):

```js
var libsignal = require('libsignal-protocol')
```

Or, you can include the prebundled `dist/libsignal.js` in your HTML file.

### Install time

At install time, a libsignal client needs to generate its identity keys,
registration id, and prekeys.

```js
var libsignal = require('libsignal-protocol')
var KeyHelper = libsignal.KeyHelper;

var registrationId = KeyHelper.generateRegistrationId();
Expand Down Expand Up @@ -112,7 +121,7 @@ var store = new MySignalProtocolStore();
var address = new libsignal.SignalProtocolAddress(recipientId, deviceId);

// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
SessionBuilder sessionBuilder = new libsignal.SessionBuilder(store, address);
var sessionBuilder = new libsignal.SessionBuilder(store, address);

// Process a prekey fetched from the server. Returns a promise that resolves
// once a session is created and saved in the store, or rejects if the
Expand Down Expand Up @@ -179,23 +188,24 @@ sessionCipher.decryptWhisperMessage(ciphertext).then(function(plaintext) {
});
```

## Building
## Contributing

To compile curve25519 from C souce files in `/native`, install
[emscripten](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
Theen, build all the assets with

```
grunt compile
grunt build
```

To test, you will need an account with [Sauce Labs](https://saucelabs.com). Get your username and API key,
then set the appropriate envirionment variables to run the tests.
To test, you can serve the project root and navigate to localhost:[port]/test to see the Mocha tests run.
For the full CI experience, you will need an account with [Sauce Labs](https://saucelabs.com).
Get your username and API key, then set the appropriate envirionment variables to run the tests:

```sh
SAUCE_USERNAME="your-sauce-username" SAUCE_ACCESS_KEY="your-sauce-key" grunt test
```


## License

Copyright 2015-2016 Open Whisper Systems
Expand Down
Loading

0 comments on commit e4debbf

Please sign in to comment.