Add emscripten-compiled curve25519 module

Build with `grunt compile && grunt concat:curve25519` after installing
emscripten.

Enable by either (a) not loading nativeclient.js or (b) setting
`textsecure.NATIVE_CLIENT = false` before loading nativeclient.js.
pull/749/head
lilia 10 years ago
parent 3d27c98845
commit b4f4f87a7c

1
.gitignore vendored

@ -1,2 +1,3 @@
node_modules
.sass-cache
build

@ -1,7 +1,8 @@
'use strict';
var child_process = require('child_process');
var util = require('util');
module.exports = function(grunt) {
var bower = grunt.file.readJSON('bower.json');
var components = [];
for (var i in bower.concat.app) {
@ -34,6 +35,15 @@ module.exports = function(grunt) {
'test/_test.js'
],
dest: 'test/test.js',
},
curve25519: {
src: [
'build/_before.js',
'build/curve25519_compiled.js',
'build/curve25519.js',
'build/_after.js'
],
dest: 'js/curve25519_compiled.js'
}
},
sass: {
@ -42,12 +52,63 @@ module.exports = function(grunt) {
'stylesheets/manifest.css': 'stylesheets/manifest.scss'
}
}
},
compile: {
curve25519_compiled: {
src_files: [
'nacl/ed25519/additions/*.c',
'nacl/curve25519-donna.c',
'nacl/ed25519/*.c',
'nacl/ed25519/sha512/sha2big.c'
],
methods: [
'curve25519_donna',
'curve25519_sign',
'curve25519_verify',
'crypto_sign_ed25519_ref10_ge_scalarmult_base',
'sph_sha512_init',
'malloc'
]
}
}
});
grunt.loadNpmTasks('grunt-preen');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.registerMultiTask('compile', 'Compile the C libraries with emscripten.', function() {
var callback = this.async();
var outfile = 'build/' + this.target + '.js';
var exported_functions = this.data.methods.map(function(name) {
return "'_" + name + "'";
});
var flags = [
'-O2',
'-Qunused-arguments',
'-o', outfile,
'-Inacl/ed25519/nacl_includes -Inacl/ed25519 -Inacl/ed25519/sha512',
'-s', "EXPORTED_FUNCTIONS=\"[" + exported_functions.join(',') + "]\""];
var command = [].concat('emcc', this.data.src_files, flags).join(' ');
grunt.log.writeln('Compiling via emscripten to ' + outfile);
var exitCode = 0;
grunt.verbose.subhead(command);
grunt.verbose.writeln(util.format('Expecting exit code %d', exitCode));
var child = child_process.exec(command);
child.stdout.on('data', function (d) { grunt.log.write(d); });
child.stderr.on('data', function (d) { grunt.log.error(d); });
child.on('exit', function(code) {
if (code !== exitCode) {
grunt.log.error(util.format('Exited with code: %d.', code));
return callback(false);
}
grunt.verbose.ok(util.format('Exited with code: %d.', code));
callback(true);
});
});
grunt.registerTask('default', ['preen', 'concat', 'sass']);
};

@ -20,6 +20,7 @@
<script type="text/javascript" src="js/components.js"></script>
<script type="text/javascript" src="js/protobufs.js"></script>
<script type="text/javascript" src="js/curve25519_compiled.js"></script>
<script type="text/javascript" src="js/nativeclient.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/storage.js"></script>

@ -0,0 +1,2 @@
})();

@ -0,0 +1 @@
;(function() {

@ -0,0 +1,113 @@
/* vim: ts=4:sw=4:expandtab
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
;(function() {
'use strict';
// Insert some bytes into the emscripten memory and return a pointer
function _allocate(bytes) {
var address = Module._malloc(bytes.length);
Module.HEAPU8.set(bytes, address);
return address;
}
function _readBytes(address, length, array) {
array.set(Module.HEAPU8.subarray(address, address + length));
}
var basepoint = new Uint8Array(32);
basepoint[0] = 9;
window.curve25519 = {
privToPub: function(privKey) {
var priv = new Uint8Array(privKey);
priv[0] &= 248;
priv[31] &= 127;
priv[31] |= 64
// Where to store the result
var pubKey = new Uint8Array(32);
//var publicKey_ptr = Module._malloc(32);
var publicKey_ptr = _allocate(pubKey);
// Get a pointer to the private key
var privateKey_ptr = _allocate(priv);
// The basepoint for generating public keys is 0x09 followed by 31 null bytes
var basepoint_ptr = _allocate(basepoint);
// The return value is just 0, the operation is done in place
var err = Module._curve25519_donna(publicKey_ptr, privateKey_ptr, basepoint_ptr);
var res = new Uint8Array(32);
_readBytes(publicKey_ptr, 32, res);
return Promise.resolve({ pubKey: res.buffer, privKey: privKey });
},
ECDHE: function(pubKey, privKey) {
// Where to store the result
var sharedKey_ptr = Module._malloc(32);
// Get a pointer to our private key
var privateKey_ptr = _allocate(new Uint8Array(privKey));
// Get a pointer to their public key, the basepoint when you're generating a shared secret
var basepoint_ptr = _allocate(new Uint8Array(pubKey));
// Return value is 0 here too of course
var err = Module._curve25519_donna(sharedKey_ptr, privateKey_ptr, basepoint_ptr);
var res = new Uint8Array(32);
_readBytes(sharedKey_ptr, 32, res);
return Promise.resolve(res.buffer);
},
Ed25519Sign: function(privKey, message) {
// Where to store the result
var signature_ptr = Module._malloc(32);
// Get a pointer to our private key
var privateKey_ptr = _allocate(new Uint8Array(privKey));
// Get a pointer to the message
var message_ptr = _allocate(new Uint8Array(message));
var err = Module._curve25519_sign(signature_ptr, privateKey_ptr, message_ptr, message.byteLength);
var res = new Uint8Array(64);
_readBytes(signature_ptr, 64, res);
return Promise.resolve(res.buffer);
},
Ed25519Verify: function(pubKey, message, sig) {
// Get a pointer to their public key
var publicKey_ptr = _allocate(new Uint8Array(pubKey));
// Get a pointer to the signature
var signature_ptr = _allocate(new Uint8Array(sig));
// Get a pointer to the message
var message_ptr = _allocate(new Uint8Array(message));
var res = Module._curve25519_verify(signature_ptr, publicKey_ptr, message_ptr, message.byteLength);
return new Promise(function(resolve, reject) {
if (res !== 0) {
reject(new Error("Invalid signature"));
}
resolve();
});
}
};
})();

@ -130,6 +130,7 @@
<script type="text/javascript" src="components/bootstrap-tagsinput/dist/bootstrap-tagsinput.js"></script>
<script type="text/javascript" src="js/protobufs.js"></script>
<script type="text/javascript" src="js/curve25519_compiled.js"></script>
<script type="text/javascript" src="js/nativeclient.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/storage.js"></script>

@ -22,9 +22,8 @@
* for all low-level crypto operations,
*/
// TODO Select an curve25519 implementation
//var curve25519 = textsecure.NATIVE_CLIENT ? textsecure.nativeclient : textsecure.tweetnacl || asmjs;
var curve25519 = textsecure.nativeclient;
var curve25519 = window.curve25519;
if (textsecure.NATIVE_CLIENT) curve25519 = textsecure.nativeclient;
window.textsecure.crypto = {
getRandomBytes: function(size) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -95,6 +95,7 @@
<script type="text/javascript" src="js/components.js"></script>
<script type="text/javascript" src="js/protobufs.js"></script>
<script type="text/javascript" src="js/curve25519_compiled.js"></script>
<script type="text/javascript" src="js/nativeclient.js"></script>
<script type="text/javascript" src="js/helpers.js"></script>
<script type="text/javascript" src="js/storage.js"></script>

@ -15,6 +15,7 @@
*/
'use strict';
window.assert = chai.assert;
describe("Crypto", function() {
describe("Encrypt AES-CBC", function() {

@ -0,0 +1,28 @@
/* vim: ts=4:sw=4
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
describe('curve25519_compiled.js', function() {
describe('curve25519_donna', function() {
it('exists', function() {
var curve25519_donna = Module.cwrap('curve25519_sign', 'string', 'string');
assert.isDefined(Module.cwrap);
});
});
test_curve25519_implementation(curve25519);
});

@ -142,6 +142,7 @@
<script type="text/javascript" src="../js/sendmessage.js" data-cover></script>
<script type="text/javascript" src="../js/chromium.js"></script>
<script type="text/javascript" src="../js/curve25519_compiled.js"></script>
<script type="text/javascript" src="../js/views/notifications.js"></script>
<script type="text/javascript" src="../js/views/list_view.js" data-cover></script>
@ -163,5 +164,6 @@
<script type="text/javascript" src="views/message_view_test.js"></script>
<script type="text/javascript" src="views/list_view_test.js"></script>
<script type="text/javascript" src="views/threads_test.js"></script>
<script type="text/javascript" src="curve25519_compiled_test.js"></script>
</body>
</html>

Loading…
Cancel
Save