aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Haines <mark.haines@matrix.org>2015-06-23 17:50:30 +0100
committerMark Haines <mark.haines@matrix.org>2015-06-23 17:50:30 +0100
commit07072912cdf680766d8c36f6333b77f9aa4f027c (patch)
treed809adaa9555ed3bb280013a3ce0d93e4d5bda13
parentf10c04d62d15cc42af4683c2020cf5dffe895bd2 (diff)
Add javascript bindings using emscripten
-rw-r--r--javascript/axolotl_post.js247
-rw-r--r--javascript/axolotl_pre.js1
-rw-r--r--javascript/build.py69
3 files changed, 317 insertions, 0 deletions
diff --git a/javascript/axolotl_post.js b/javascript/axolotl_post.js
new file mode 100644
index 0000000..7902725
--- /dev/null
+++ b/javascript/axolotl_post.js
@@ -0,0 +1,247 @@
+var runtime = Module['Runtime'];
+var malloc = Module['_malloc'];
+var free = Module['_free'];
+var Pointer_stringify = Module['Pointer_stringify'];
+var AXOLOTL_ERROR = Module['_axolotl_error']();
+
+function stack(size_or_array) {
+ return Module['allocate'](size_or_array, 'i8', Module['ALLOC_STACK']);
+}
+
+function array_from_string(string) {
+ return Module['intArrayFromString'](string, true);
+}
+
+function random_stack(size) {
+ var ptr = stack(size);
+ var array = new Uint8Array(Module['HEAPU8'].buffer, ptr, size);
+ window.crypto.getRandomValues(array);
+}
+
+function restore_stack(wrapped) {
+ return function() {
+ var sp = runtime.stackSave();
+ try {
+ return wrapped.apply(this, arguments);
+ } finally {
+ runtime.stackRestore(sp);
+ }
+ }
+}
+
+function Account() {
+ var size = Module['_axolotl_account_size']();
+ this.buf = malloc(size);
+ this.ptr = Module['_axolotl_account'](this.buf);
+}
+
+function account_method(wrapped) {
+ return function() {
+ var result = wrapped.apply(this, arguments);
+ if (result === AXOLOTL_ERROR) {
+ var message = Pointer_stringify(
+ Module['_axolotl_account_last_error'](arguments[0])
+ );
+ throw "AXOLOTL." + message;
+ }
+ return result;
+ }
+}
+
+Account.prototype['free'] = function() {
+ free(this.ptr);
+}
+
+Account.prototype['create'] = restore_stack(function() {
+ var random_length = account_method(
+ Module['_axolotl_create_account_random_length']
+ )(this.ptr);
+ var random = random_stack(random_length);
+ account_method(Module['_axolotl_create_account'])(
+ this.ptr, random, random_length
+ );
+});
+
+Account.prototype['identity_keys'] = restore_stack(function() {
+ var keys_length = account_method(
+ Module['_axolotl_account_identity_keys_length']
+ )(this.ptr);
+ var keys = stack(keys_length);
+ account_method(Module['_axolotl_account_identity_keys'])(
+ this.ptr, keys, keys_length
+ );
+ return Pointer_stringify(keys, keys_length);
+});
+
+Account.prototype['one_time_keys'] = restore_stack(function() {
+ var keys_length = account_method(
+ Module['_axolotl_account_one_time_keys_length']
+ )(this.ptr);
+ var keys = stack(keys_length);
+ account_method(Module['_axolotl_account_one_time_keys'])(
+ this.ptr, keys, keys_length
+ );
+ return Pointer_stringify(keys, keys_length);
+});
+
+Account.prototype['pickle'] = restore_stack(function(key) {
+ var key_array = array_from_string(key);
+ var pickle_length = account_method(
+ Module['_axolotl_pickle_account_length']
+ )(this.ptr);
+ var key_buffer = stack(key_array);
+ var pickle_buffer = stack(pickle_length);
+ account_method(Module['_axolotl_pickle_account'])(
+ this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+ );
+ return Pointer_stringify(pickle_buffer, pickle_length);
+});
+
+Account.prototype['unpickle'] = restore_stack(function(key, pickle) {
+ var key_array = array_from_string(key);
+ var key_buffer = stack(key_array);
+ var pickle_array = array_from_string(pickle);
+ var pickle_buffer = stack(pickle_length);
+ account_method(Module['_axolotl_unpickle_account'])(
+ this.ptr, key_buffer, key_array.length, pickle_buffer,
+ pickle_array.length
+ );
+});
+
+function Session() {
+ var size = Module['_axolotl_session_size']();
+ this.buf = malloc(size);
+ this.ptr = Module['_axolotl_session'](this.buf);
+}
+
+function session_method(wrapped) {
+ return function() {
+ var result = wrapped.apply(this, arguments);
+ if (result === AXOLOTL_ERROR) {
+ var message = Pointer_stringify(
+ Module['_axolotl_session_last_error'](arguments[0])
+ );
+ throw "AXOLOTL." + message;
+ }
+ return result;
+ }
+}
+
+Session.prototype['free'] = function() {
+ free(this.ptr);
+}
+
+Session.prototype['pickle'] = restore_stack(function(key) {
+ var key_array = array_from_string(key);
+ var pickle_length = session_method(
+ Module['_axolotl_pickle_session_length']
+ )(this.ptr);
+ var key_buffer = stack(key_array);
+ var pickle_buffer = stack(pickle_length);
+ session_method(Module['_axolotl_pickle_session'])(
+ this.ptr, key_buffer, key_array.length, pickle_buffer, pickle_length
+ );
+ return Pointer_stringify(pickle_buffer, pickle_length);
+});
+
+Session.prototype['unpickle'] = restore_stack(function(key, pickle) {
+ var key_array = array_from_string(key);
+ var key_buffer = stack(key_array);
+ var pickle_array = array_from_string(pickle);
+ var pickle_buffer = stack(pickle_array);
+ session_method(Module['_axolotl_unpickle_session'])(
+ this.ptr, key_buffer, key_array.length, pickle_buffer,
+ pickle_array.length
+ );
+});
+
+Session.prototype['create_outbound'] = restore_stack(function(
+ account, their_identity_key, their_one_time_key_id, their_one_time_key
+) {
+ var random_length = session_method(
+ Module['_axolotl_create_outbound_session_random_length']
+ )(this.ptr);
+ var random = random_stack(random_length);
+ var identity_key_array = array_from_string(their_identity_key);
+ var one_time_key_array = array_from_string(their_one_time_key);
+ var identity_key_buffer = stack(identity_key_array);
+ var one_time_key_buffer = stack(one_time_key_array);
+ session_method(Module['_axolotl_create_outbound_session'])(
+ this.ptr, account.ptr,
+ identity_key_buffer, identity_key_array.length,
+ their_one_time_key_id,
+ one_time_key_buffer, one_time_key_array.length,
+ random, random_length
+ );
+});
+
+Session.prototype['create_inbound'] = restore_stack(function(
+ account, one_time_key_message
+) {
+ var message_array = array_from_string(one_time_key_message);
+ var message_buffer = stack(message_array);
+ session_method(Module['_axolotl_create_inbound_session'])(
+ this.ptr, account.ptr, message_buffer, message_array.length
+ );
+});
+
+Session.prototype['matches_inbound'] = restore_stack(function(
+ account, one_time_key_message
+) {
+ var message_array = array_from_string(one_time_key_message);
+ var message_buffer = stack(message_array);
+ return session_method(Module['_axolotl_matches_inbound_session'])(
+ this.ptr, account.ptr, message_buffer, message_array.length
+ ) ? true : false;
+});
+
+Session.prototype['encrypt'] = restore_stack(function(
+ plaintext
+) {
+ var random_length = session_method(
+ Module['_axolotl_encrypt_random_length']
+ )(this.ptr);
+ var message_type = session_method(
+ Module['_axolotl_encrypt_message_type']
+ )(this.ptr);
+ var plaintext_array = array_from_string(plaintext);
+ var message_length = session_method(
+ Module['_axolotl_encrypt_message_length']
+ )(this.ptr, plaintext_array.length);
+ var random = random_stack(random_length);
+ var plaintext_buffer = stack(plaintext_array);
+ var message_buffer = stack(message_length);
+ session_method(Module['_axolotl_encrypt'])(
+ this.ptr,
+ plaintext_buffer, plaintext_array.length,
+ random, random_length,
+ message_buffer, message_length
+ );
+ return {
+ "type": message_type,
+ "body": Pointer_stringify(message_buffer, message_length)
+ };
+});
+
+Session.prototype['decrypt'] = restore_stack(function(
+ message_type, message
+) {
+ var message_array = array_from_string(message);
+ var message_buffer = stack(message_array);
+ var max_plaintext_length = session_method(
+ Module['_axolotl_decrypt_max_plaintext_length']
+ )(this.ptr, message_type, message_buffer, message_array.length);
+ // caculating the length destroys the input buffer.
+ // So we copy the array to a new buffer
+ var message_buffer = stack(message_array);
+ var plaintext_buffer = stack(max_plaintext_length);
+ var plaintext_length = session_method(Module["_axolotl_decrypt"])(
+ this.ptr, message_type,
+ message_buffer, message.length,
+ plaintext_buffer, max_plaintext_length
+ );
+ return Pointer_stringify(plaintext_buffer, plaintext_length);
+});
+
+return {"Account": Account, "Session": Session};
+}();
diff --git a/javascript/axolotl_pre.js b/javascript/axolotl_pre.js
new file mode 100644
index 0000000..69df923
--- /dev/null
+++ b/javascript/axolotl_pre.js
@@ -0,0 +1 @@
+Axolotl = function() {
diff --git a/javascript/build.py b/javascript/build.py
new file mode 100644
index 0000000..2b754e9
--- /dev/null
+++ b/javascript/build.py
@@ -0,0 +1,69 @@
+#! /usr/bin/python
+# Copyright 2015 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import subprocess
+import glob
+import os
+import sys
+import re
+import json
+
+source_files = glob.glob("src/*.cpp")
+pre_js, = glob.glob("javascript/*pre.js")
+post_js, = glob.glob("javascript/*post.js")
+
+
+functions = set()
+RE_FUNCTION=re.compile("(axolotl_[^( ]*)\\(")
+with open("include/axolotl/axolotl.hh") as header:
+ for line in header:
+ match = RE_FUNCTION.search(line)
+ if match:
+ functions.add(match.groups()[0])
+
+
+exported_functions = os.path.abspath("build/exported_functions.json")
+with open(exported_functions, "w") as json_file:
+ json.dump(["_" + function for function in functions], json_file)
+
+
+emcc = os.environ.get("EMCC", "emcc")
+
+compile_args = [emcc]
+compile_args += """
+ -O3
+ -Iinclude
+ -Ilib
+ -std=c++11
+ --closure 1
+ --memory-init-file 0
+ -s NO_FILESYSTEM=1
+ -s NO_BROWSER=1
+ -s INVOKE_RUN=0
+""".split()
+compile_args += source_files
+compile_args += ("--pre-js", pre_js)
+compile_args += ("--post-js", post_js)
+compile_args += ("-s", "EXPORTED_FUNCTIONS=@" + exported_functions)
+
+library = "build/axolotl.js"
+
+def run(args):
+ print args
+ print " ".join(args)
+ subprocess.check_call(args)
+
+run(compile_args + ["-o", library])
+