From 15e964de81a531c2f919050df7b1fcda492701de Mon Sep 17 00:00:00 2001 From: lilia Date: Wed, 18 May 2016 12:45:33 -0700 Subject: [PATCH] Update libsignal-protocol v0.13.0 * libsignal.Curve is now a synchronous interface. * libsignal.Curve.async is now the asynchronous interface. * Fixes bugs in session management. // FREEBIE --- js/libsignal-protocol-worker.js | 83 +++++--- js/libtextsecure.js | 299 ++++++++++++++++------------ libtextsecure/ProvisioningCipher.js | 6 +- libtextsecure/libsignal-protocol.js | 293 ++++++++++++++++----------- 4 files changed, 406 insertions(+), 275 deletions(-) diff --git a/js/libsignal-protocol-worker.js b/js/libsignal-protocol-worker.js index ae098591..ac536dff 100644 --- a/js/libsignal-protocol-worker.js +++ b/js/libsignal-protocol-worker.js @@ -1,4 +1,6 @@ ;(function(){ +var Internal = {}; +var libsignal = {}; // The Module object: Our interface to the outside world. We import // and export values on it, and do the work to get that through // closure compiler if necessary. There are various ways Module can be used: @@ -25148,7 +25150,7 @@ run(); */ var Internal = Internal || {}; -Internal.curve25519 = function() { +(function() { 'use strict'; // Insert some bytes into the emscripten memory and return a pointer @@ -25166,38 +25168,35 @@ Internal.curve25519 = function() { var basepoint = new Uint8Array(32); basepoint[0] = 9; - return { + Internal.curve25519 = { keyPair: function(privKey) { - return new Promise(function(resolve) { - var priv = new Uint8Array(privKey); - priv[0] &= 248; - priv[31] &= 127; - priv[31] |= 64 + var priv = new Uint8Array(privKey); + priv[0] &= 248; + priv[31] &= 127; + priv[31] |= 64 - // Where to store the result - var publicKey_ptr = Module._malloc(32); + // Where to store the result + var publicKey_ptr = Module._malloc(32); - // Get a pointer to the private key - var privateKey_ptr = _allocate(priv); + // Get a pointer to the private key + var privateKey_ptr = _allocate(priv); - // The basepoint for generating public keys - var basepoint_ptr = _allocate(basepoint); + // The basepoint for generating public keys + 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); + // 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); + var res = new Uint8Array(32); + _readBytes(publicKey_ptr, 32, res); - Module._free(publicKey_ptr); - Module._free(privateKey_ptr); - Module._free(basepoint_ptr); - - resolve({ pubKey: res.buffer, privKey: privKey }); - }); + Module._free(publicKey_ptr); + Module._free(privateKey_ptr); + Module._free(basepoint_ptr); + return { pubKey: res.buffer, privKey: priv.buffer }; }, sharedSecret: function(pubKey, privKey) { // Where to store the result @@ -25222,7 +25221,7 @@ Internal.curve25519 = function() { Module._free(privateKey_ptr); Module._free(basepoint_ptr); - return Promise.resolve(res.buffer); + return res.buffer; }, sign: function(privKey, message) { // Where to store the result @@ -25246,7 +25245,7 @@ Internal.curve25519 = function() { Module._free(privateKey_ptr); Module._free(message_ptr); - return Promise.resolve(res.buffer); + return res.buffer; }, verify: function(pubKey, message, sig) { // Get a pointer to their public key @@ -25267,21 +25266,43 @@ Internal.curve25519 = function() { Module._free(signature_ptr); Module._free(message_ptr); + return res !== 0; + } + }; + + Internal.curve25519_async = { + keyPair: function(privKey) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.keyPair(privKey)); + }); + }, + sharedSecret: function(pubKey, privKey) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.sharedSecret(pubKey, privKey)); + }); + }, + sign: function(privKey, message) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.sign(privKey, message)); + }); + }, + verify: function(pubKey, message, sig) { return new Promise(function(resolve, reject) { - if (res !== 0) { + if (Internal.curve25519.verify(pubKey, message, sig)) { reject(new Error("Invalid signature")); } else { resolve(); } }); - } + }, }; -}(); + +})(); var Internal = Internal || {}; // I am the worker this.onmessage = function(e) { - Internal.curve25519[e.data.methodName].apply(null, e.data.args).then(function(result) { + Internal.curve25519_async[e.data.methodName].apply(null, e.data.args).then(function(result) { postMessage({ id: e.data.id, result: result }); }).catch(function(error) { postMessage({ id: e.data.id, error: error.message }); diff --git a/js/libtextsecure.js b/js/libtextsecure.js index e6e906e4..102434b9 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -113,6 +113,8 @@ })(); ;(function(){ +var Internal = {}; +window.libsignal = {}; // The Module object: Our interface to the outside world. We import // and export values on it, and do the work to get that through // closure compiler if necessary. There are various ways Module can be used: @@ -25262,7 +25264,7 @@ run(); */ var Internal = Internal || {}; -Internal.curve25519 = function() { +(function() { 'use strict'; // Insert some bytes into the emscripten memory and return a pointer @@ -25280,38 +25282,35 @@ Internal.curve25519 = function() { var basepoint = new Uint8Array(32); basepoint[0] = 9; - return { + Internal.curve25519 = { keyPair: function(privKey) { - return new Promise(function(resolve) { - var priv = new Uint8Array(privKey); - priv[0] &= 248; - priv[31] &= 127; - priv[31] |= 64 + var priv = new Uint8Array(privKey); + priv[0] &= 248; + priv[31] &= 127; + priv[31] |= 64 - // Where to store the result - var publicKey_ptr = Module._malloc(32); + // Where to store the result + var publicKey_ptr = Module._malloc(32); - // Get a pointer to the private key - var privateKey_ptr = _allocate(priv); + // Get a pointer to the private key + var privateKey_ptr = _allocate(priv); - // The basepoint for generating public keys - var basepoint_ptr = _allocate(basepoint); + // The basepoint for generating public keys + 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); + // 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); + var res = new Uint8Array(32); + _readBytes(publicKey_ptr, 32, res); - Module._free(publicKey_ptr); - Module._free(privateKey_ptr); - Module._free(basepoint_ptr); - - resolve({ pubKey: res.buffer, privKey: privKey }); - }); + Module._free(publicKey_ptr); + Module._free(privateKey_ptr); + Module._free(basepoint_ptr); + return { pubKey: res.buffer, privKey: priv.buffer }; }, sharedSecret: function(pubKey, privKey) { // Where to store the result @@ -25336,7 +25335,7 @@ Internal.curve25519 = function() { Module._free(privateKey_ptr); Module._free(basepoint_ptr); - return Promise.resolve(res.buffer); + return res.buffer; }, sign: function(privKey, message) { // Where to store the result @@ -25360,7 +25359,7 @@ Internal.curve25519 = function() { Module._free(privateKey_ptr); Module._free(message_ptr); - return Promise.resolve(res.buffer); + return res.buffer; }, verify: function(pubKey, message, sig) { // Get a pointer to their public key @@ -25381,36 +25380,55 @@ Internal.curve25519 = function() { Module._free(signature_ptr); Module._free(message_ptr); + return res !== 0; + } + }; + + Internal.curve25519_async = { + keyPair: function(privKey) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.keyPair(privKey)); + }); + }, + sharedSecret: function(pubKey, privKey) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.sharedSecret(pubKey, privKey)); + }); + }, + sign: function(privKey, message) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.sign(privKey, message)); + }); + }, + verify: function(pubKey, message, sig) { return new Promise(function(resolve, reject) { - if (res !== 0) { + if (Internal.curve25519.verify(pubKey, message, sig)) { reject(new Error("Invalid signature")); } else { resolve(); } }); - } + }, }; -}(); + +})(); ;(function() { 'use strict'; -window.libsignal = window.libsignal || {}; - -var Internal = Internal || {}; // I am the...workee? -var origCurve25519 = Internal.curve25519; +var origCurve25519 = Internal.curve25519_async; Internal.startWorker = function(url) { Internal.stopWorker(); // there can be only one - Internal.curve25519 = new Curve25519Worker(url); + Internal.curve25519_async = new Curve25519Worker(url); }; Internal.stopWorker = function() { - if (Internal.curve25519 instanceof Curve25519Worker) { - var worker = Internal.curve25519.worker; - Internal.curve25519 = origCurve25519; + if (Internal.curve25519_async instanceof Curve25519Worker) { + var worker = Internal.curve25519_async.worker; + Internal.curve25519_async = origCurve25519; worker.terminate(); } }; @@ -35208,6 +35226,116 @@ Curve25519Worker.prototype = { return ProtoBuf; }); +(function() { + 'use strict'; + + function validatePrivKey(privKey) { + if (privKey === undefined || !(privKey instanceof ArrayBuffer) || privKey.byteLength != 32) { + throw new Error("Invalid private key"); + } + } + function validatePubKeyFormat(pubKey) { + if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32)) { + throw new Error("Invalid public key"); + } + if (pubKey.byteLength == 33) { + return pubKey.slice(1); + } else { + console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey"); + return pubKey; + } + } + + function processKeys(raw_keys) { + // prepend version byte + var origPub = new Uint8Array(raw_keys.pubKey); + var pub = new Uint8Array(33); + pub.set(origPub, 1); + pub[0] = 5; + + return { pubKey: pub.buffer, privKey: raw_keys.privKey }; + } + + function wrapCurve25519(curve25519) { + return { + // Curve 25519 crypto + createKeyPair: function(privKey) { + validatePrivKey(privKey); + var raw_keys = curve25519.keyPair(privKey); + if (raw_keys instanceof Promise) { + return raw_keys.then(processKeys); + } else { + return processKeys(raw_keys); + } + }, + ECDHE: function(pubKey, privKey) { + pubKey = validatePubKeyFormat(pubKey); + validatePrivKey(privKey); + + if (pubKey === undefined || pubKey.byteLength != 32) { + throw new Error("Invalid public key"); + } + + return curve25519.sharedSecret(pubKey, privKey); + }, + Ed25519Sign: function(privKey, message) { + validatePrivKey(privKey); + + if (message === undefined) { + throw new Error("Invalid message"); + } + + return curve25519.sign(privKey, message); + }, + Ed25519Verify: function(pubKey, msg, sig) { + pubKey = validatePubKeyFormat(pubKey); + + if (pubKey === undefined || pubKey.byteLength != 32) { + throw new Error("Invalid public key"); + } + + if (msg === undefined) { + throw new Error("Invalid message"); + } + + if (sig === undefined || sig.byteLength != 64) { + throw new Error("Invalid signature"); + } + + return curve25519.verify(pubKey, msg, sig); + } + }; + }; + + Internal.Curve = wrapCurve25519(Internal.curve25519); + Internal.Curve.async = wrapCurve25519(Internal.curve25519_async); + + function wrapCurve(curve) { + return { + generateKeyPair: function() { + var privKey = Internal.crypto.getRandomBytes(32); + return curve.createKeyPair(privKey); + }, + createKeyPair: function(privKey) { + return curve.createKeyPair(privKey); + }, + calculateAgreement: function(pubKey, privKey) { + return curve.ECDHE(pubKey, privKey); + }, + verifySignature: function(pubKey, msg, sig) { + return curve.Ed25519Verify(pubKey, msg, sig); + }, + calculateSignature: function(privKey, message) { + return curve.Ed25519Sign(privKey, message); + } + }; + } + + libsignal.Curve = wrapCurve(Internal.Curve); + libsignal.Curve.async = wrapCurve(Internal.Curve.async); + +})(); + /* vim: ts=4:sw=4 * * This program is free software: you can redistribute it and/or modify @@ -35234,19 +35362,6 @@ var Internal = Internal || {}; throw new Error('WebCrypto not found'); } - - function validatePubKeyFormat(pubKey) { - if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32)) { - throw new Error("Invalid public key"); - } - if (pubKey.byteLength == 33) { - return pubKey.slice(1); - } else { - console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey"); - return pubKey; - } - } - Internal.crypto = { getRandomBytes: function(size) { var array = new Uint8Array(size); @@ -35296,62 +35411,20 @@ var Internal = Internal || {}; if (privKey === undefined) { privKey = Internal.crypto.getRandomBytes(32); } - if (privKey.byteLength != 32) { - throw new Error("Invalid private key"); - } - - return Internal.curve25519.keyPair(privKey).then(function(raw_keys) { - // prepend version byte - var origPub = new Uint8Array(raw_keys.pubKey); - var pub = new Uint8Array(33); - pub.set(origPub, 1); - pub[0] = 5; - - return { pubKey: pub.buffer, privKey: raw_keys.privKey }; - }); + return Internal.Curve.async.createKeyPair(privKey); }, ECDHE: function(pubKey, privKey) { - pubKey = validatePubKeyFormat(pubKey); - if (privKey === undefined || privKey.byteLength != 32) { - throw new Error("Invalid private key"); - } - - if (pubKey === undefined || pubKey.byteLength != 32) { - throw new Error("Invalid public key"); - } - - return Internal.curve25519.sharedSecret(pubKey, privKey); + return Internal.Curve.async.ECDHE(pubKey, privKey); }, Ed25519Sign: function(privKey, message) { - if (privKey === undefined || privKey.byteLength != 32) { - throw new Error("Invalid private key"); - } - - if (message === undefined) { - throw new Error("Invalid message"); - } - - return Internal.curve25519.sign(privKey, message); + return Internal.Curve.async.Ed25519Sign(privKey, message); }, Ed25519Verify: function(pubKey, msg, sig) { - pubKey = validatePubKeyFormat(pubKey); - - if (pubKey === undefined || pubKey.byteLength != 32) { - throw new Error("Invalid public key"); - } - - if (msg === undefined) { - throw new Error("Invalid message"); - } - - if (sig === undefined || sig.byteLength != 64) { - throw new Error("Invalid signature"); - } - - return Internal.curve25519.verify(pubKey, msg, sig); + return Internal.Curve.async.Ed25519Verify(pubKey, msg, sig); } }; + // HKDF for TextSecure has a bit of additional handling - salts always end up being 32 bytes Internal.HKDF = function(input, salt, info) { if (salt.byteLength != 32) { @@ -35378,24 +35451,6 @@ var Internal = Internal || {}; }); }; - libsignal.Curve = { - generateKeyPair: function() { - return Internal.crypto.createKeyPair(); - }, - createKeyPair: function(privKey) { - return Internal.crypto.createKeyPair(privKey); - }, - calculateAgreement: function(pubKey, privKey) { - return Internal.crypto.ECDHE(pubKey, privKey); - }, - verifySignature: function(pubKey, msg, sig) { - return Internal.crypto.Ed25519Verify(pubKey, msg, sig); - }, - calculateSignature: function(privKey, message) { - return Internal.crypto.Ed25519Sign(privKey, message); - }, - }; - libsignal.HKDF = { deriveSecrets: function(input, salt, info) { return Internal.HKDF(input, salt, info); @@ -35895,8 +35950,8 @@ Internal.SessionRecord = function() { oldestSession = session; } } - console.log("Deleting session closed at", session.indexInfo.closed); - delete this.sessions[util.toString(oldestBaseKey)]; + console.log("Deleting session closed at", oldestSession.indexInfo.closed); + delete sessions[util.toString(oldestBaseKey)]; } }, }; @@ -36536,7 +36591,7 @@ SessionCipher.prototype = { libsignal.SessionCipher = function(storage, remoteAddress) { var cipher = new SessionCipher(storage, remoteAddress); - // returns a Promise that resolves to a ciphertext array buffer + // returns a Promise that resolves to a ciphertext object this.encrypt = cipher.encrypt.bind(cipher); // returns a Promise that inits a session if necessary and resolves @@ -39244,7 +39299,7 @@ ProvisioningCipher.prototype = { var ivAndCiphertext = message.slice(0, message.byteLength - 32); var ciphertext = message.slice(16 + 1, message.byteLength - 32); - return libsignal.Curve.calculateAgreement( + return libsignal.Curve.async.calculateAgreement( masterEphemeral, this.keyPair.privKey ).then(function(ecRes) { return libsignal.HKDF.deriveSecrets( @@ -39258,7 +39313,7 @@ ProvisioningCipher.prototype = { var provisionMessage = textsecure.protobuf.ProvisionMessage.decode(plaintext); var privKey = provisionMessage.identityKeyPrivate.toArrayBuffer(); - return libsignal.Curve.createKeyPair(privKey).then(function(keyPair) { + return libsignal.Curve.async.createKeyPair(privKey).then(function(keyPair) { return { identityKeyPair : keyPair, number : provisionMessage.number, @@ -39270,7 +39325,7 @@ ProvisioningCipher.prototype = { getPublicKey: function() { return Promise.resolve().then(function() { if (!this.keyPair) { - return libsignal.Curve.generateKeyPair().then(function(keyPair) { + return libsignal.Curve.async.generateKeyPair().then(function(keyPair) { this.keyPair = keyPair; }.bind(this)); } diff --git a/libtextsecure/ProvisioningCipher.js b/libtextsecure/ProvisioningCipher.js index 535f5baf..ecee4e2a 100644 --- a/libtextsecure/ProvisioningCipher.js +++ b/libtextsecure/ProvisioningCipher.js @@ -16,7 +16,7 @@ ProvisioningCipher.prototype = { var ivAndCiphertext = message.slice(0, message.byteLength - 32); var ciphertext = message.slice(16 + 1, message.byteLength - 32); - return libsignal.Curve.calculateAgreement( + return libsignal.Curve.async.calculateAgreement( masterEphemeral, this.keyPair.privKey ).then(function(ecRes) { return libsignal.HKDF.deriveSecrets( @@ -30,7 +30,7 @@ ProvisioningCipher.prototype = { var provisionMessage = textsecure.protobuf.ProvisionMessage.decode(plaintext); var privKey = provisionMessage.identityKeyPrivate.toArrayBuffer(); - return libsignal.Curve.createKeyPair(privKey).then(function(keyPair) { + return libsignal.Curve.async.createKeyPair(privKey).then(function(keyPair) { return { identityKeyPair : keyPair, number : provisionMessage.number, @@ -42,7 +42,7 @@ ProvisioningCipher.prototype = { getPublicKey: function() { return Promise.resolve().then(function() { if (!this.keyPair) { - return libsignal.Curve.generateKeyPair().then(function(keyPair) { + return libsignal.Curve.async.generateKeyPair().then(function(keyPair) { this.keyPair = keyPair; }.bind(this)); } diff --git a/libtextsecure/libsignal-protocol.js b/libtextsecure/libsignal-protocol.js index 980c6297..8afdd31d 100644 --- a/libtextsecure/libsignal-protocol.js +++ b/libtextsecure/libsignal-protocol.js @@ -1,4 +1,6 @@ ;(function(){ +var Internal = {}; +window.libsignal = {}; // The Module object: Our interface to the outside world. We import // and export values on it, and do the work to get that through // closure compiler if necessary. There are various ways Module can be used: @@ -25148,7 +25150,7 @@ run(); */ var Internal = Internal || {}; -Internal.curve25519 = function() { +(function() { 'use strict'; // Insert some bytes into the emscripten memory and return a pointer @@ -25166,38 +25168,35 @@ Internal.curve25519 = function() { var basepoint = new Uint8Array(32); basepoint[0] = 9; - return { + Internal.curve25519 = { keyPair: function(privKey) { - return new Promise(function(resolve) { - var priv = new Uint8Array(privKey); - priv[0] &= 248; - priv[31] &= 127; - priv[31] |= 64 + var priv = new Uint8Array(privKey); + priv[0] &= 248; + priv[31] &= 127; + priv[31] |= 64 - // Where to store the result - var publicKey_ptr = Module._malloc(32); + // Where to store the result + var publicKey_ptr = Module._malloc(32); - // Get a pointer to the private key - var privateKey_ptr = _allocate(priv); + // Get a pointer to the private key + var privateKey_ptr = _allocate(priv); - // The basepoint for generating public keys - var basepoint_ptr = _allocate(basepoint); + // The basepoint for generating public keys + 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); + // 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); + var res = new Uint8Array(32); + _readBytes(publicKey_ptr, 32, res); - Module._free(publicKey_ptr); - Module._free(privateKey_ptr); - Module._free(basepoint_ptr); - - resolve({ pubKey: res.buffer, privKey: privKey }); - }); + Module._free(publicKey_ptr); + Module._free(privateKey_ptr); + Module._free(basepoint_ptr); + return { pubKey: res.buffer, privKey: priv.buffer }; }, sharedSecret: function(pubKey, privKey) { // Where to store the result @@ -25222,7 +25221,7 @@ Internal.curve25519 = function() { Module._free(privateKey_ptr); Module._free(basepoint_ptr); - return Promise.resolve(res.buffer); + return res.buffer; }, sign: function(privKey, message) { // Where to store the result @@ -25246,7 +25245,7 @@ Internal.curve25519 = function() { Module._free(privateKey_ptr); Module._free(message_ptr); - return Promise.resolve(res.buffer); + return res.buffer; }, verify: function(pubKey, message, sig) { // Get a pointer to their public key @@ -25267,36 +25266,55 @@ Internal.curve25519 = function() { Module._free(signature_ptr); Module._free(message_ptr); + return res !== 0; + } + }; + + Internal.curve25519_async = { + keyPair: function(privKey) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.keyPair(privKey)); + }); + }, + sharedSecret: function(pubKey, privKey) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.sharedSecret(pubKey, privKey)); + }); + }, + sign: function(privKey, message) { + return new Promise(function(resolve) { + resolve(Internal.curve25519.sign(privKey, message)); + }); + }, + verify: function(pubKey, message, sig) { return new Promise(function(resolve, reject) { - if (res !== 0) { + if (Internal.curve25519.verify(pubKey, message, sig)) { reject(new Error("Invalid signature")); } else { resolve(); } }); - } + }, }; -}(); + +})(); ;(function() { 'use strict'; -window.libsignal = window.libsignal || {}; - -var Internal = Internal || {}; // I am the...workee? -var origCurve25519 = Internal.curve25519; +var origCurve25519 = Internal.curve25519_async; Internal.startWorker = function(url) { Internal.stopWorker(); // there can be only one - Internal.curve25519 = new Curve25519Worker(url); + Internal.curve25519_async = new Curve25519Worker(url); }; Internal.stopWorker = function() { - if (Internal.curve25519 instanceof Curve25519Worker) { - var worker = Internal.curve25519.worker; - Internal.curve25519 = origCurve25519; + if (Internal.curve25519_async instanceof Curve25519Worker) { + var worker = Internal.curve25519_async.worker; + Internal.curve25519_async = origCurve25519; worker.terminate(); } }; @@ -35094,6 +35112,116 @@ Curve25519Worker.prototype = { return ProtoBuf; }); +(function() { + 'use strict'; + + function validatePrivKey(privKey) { + if (privKey === undefined || !(privKey instanceof ArrayBuffer) || privKey.byteLength != 32) { + throw new Error("Invalid private key"); + } + } + function validatePubKeyFormat(pubKey) { + if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32)) { + throw new Error("Invalid public key"); + } + if (pubKey.byteLength == 33) { + return pubKey.slice(1); + } else { + console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey"); + return pubKey; + } + } + + function processKeys(raw_keys) { + // prepend version byte + var origPub = new Uint8Array(raw_keys.pubKey); + var pub = new Uint8Array(33); + pub.set(origPub, 1); + pub[0] = 5; + + return { pubKey: pub.buffer, privKey: raw_keys.privKey }; + } + + function wrapCurve25519(curve25519) { + return { + // Curve 25519 crypto + createKeyPair: function(privKey) { + validatePrivKey(privKey); + var raw_keys = curve25519.keyPair(privKey); + if (raw_keys instanceof Promise) { + return raw_keys.then(processKeys); + } else { + return processKeys(raw_keys); + } + }, + ECDHE: function(pubKey, privKey) { + pubKey = validatePubKeyFormat(pubKey); + validatePrivKey(privKey); + + if (pubKey === undefined || pubKey.byteLength != 32) { + throw new Error("Invalid public key"); + } + + return curve25519.sharedSecret(pubKey, privKey); + }, + Ed25519Sign: function(privKey, message) { + validatePrivKey(privKey); + + if (message === undefined) { + throw new Error("Invalid message"); + } + + return curve25519.sign(privKey, message); + }, + Ed25519Verify: function(pubKey, msg, sig) { + pubKey = validatePubKeyFormat(pubKey); + + if (pubKey === undefined || pubKey.byteLength != 32) { + throw new Error("Invalid public key"); + } + + if (msg === undefined) { + throw new Error("Invalid message"); + } + + if (sig === undefined || sig.byteLength != 64) { + throw new Error("Invalid signature"); + } + + return curve25519.verify(pubKey, msg, sig); + } + }; + }; + + Internal.Curve = wrapCurve25519(Internal.curve25519); + Internal.Curve.async = wrapCurve25519(Internal.curve25519_async); + + function wrapCurve(curve) { + return { + generateKeyPair: function() { + var privKey = Internal.crypto.getRandomBytes(32); + return curve.createKeyPair(privKey); + }, + createKeyPair: function(privKey) { + return curve.createKeyPair(privKey); + }, + calculateAgreement: function(pubKey, privKey) { + return curve.ECDHE(pubKey, privKey); + }, + verifySignature: function(pubKey, msg, sig) { + return curve.Ed25519Verify(pubKey, msg, sig); + }, + calculateSignature: function(privKey, message) { + return curve.Ed25519Sign(privKey, message); + } + }; + } + + libsignal.Curve = wrapCurve(Internal.Curve); + libsignal.Curve.async = wrapCurve(Internal.Curve.async); + +})(); + /* vim: ts=4:sw=4 * * This program is free software: you can redistribute it and/or modify @@ -35120,19 +35248,6 @@ var Internal = Internal || {}; throw new Error('WebCrypto not found'); } - - function validatePubKeyFormat(pubKey) { - if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32)) { - throw new Error("Invalid public key"); - } - if (pubKey.byteLength == 33) { - return pubKey.slice(1); - } else { - console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey"); - return pubKey; - } - } - Internal.crypto = { getRandomBytes: function(size) { var array = new Uint8Array(size); @@ -35182,62 +35297,20 @@ var Internal = Internal || {}; if (privKey === undefined) { privKey = Internal.crypto.getRandomBytes(32); } - if (privKey.byteLength != 32) { - throw new Error("Invalid private key"); - } - - return Internal.curve25519.keyPair(privKey).then(function(raw_keys) { - // prepend version byte - var origPub = new Uint8Array(raw_keys.pubKey); - var pub = new Uint8Array(33); - pub.set(origPub, 1); - pub[0] = 5; - - return { pubKey: pub.buffer, privKey: raw_keys.privKey }; - }); + return Internal.Curve.async.createKeyPair(privKey); }, ECDHE: function(pubKey, privKey) { - pubKey = validatePubKeyFormat(pubKey); - if (privKey === undefined || privKey.byteLength != 32) { - throw new Error("Invalid private key"); - } - - if (pubKey === undefined || pubKey.byteLength != 32) { - throw new Error("Invalid public key"); - } - - return Internal.curve25519.sharedSecret(pubKey, privKey); + return Internal.Curve.async.ECDHE(pubKey, privKey); }, Ed25519Sign: function(privKey, message) { - if (privKey === undefined || privKey.byteLength != 32) { - throw new Error("Invalid private key"); - } - - if (message === undefined) { - throw new Error("Invalid message"); - } - - return Internal.curve25519.sign(privKey, message); + return Internal.Curve.async.Ed25519Sign(privKey, message); }, Ed25519Verify: function(pubKey, msg, sig) { - pubKey = validatePubKeyFormat(pubKey); - - if (pubKey === undefined || pubKey.byteLength != 32) { - throw new Error("Invalid public key"); - } - - if (msg === undefined) { - throw new Error("Invalid message"); - } - - if (sig === undefined || sig.byteLength != 64) { - throw new Error("Invalid signature"); - } - - return Internal.curve25519.verify(pubKey, msg, sig); + return Internal.Curve.async.Ed25519Verify(pubKey, msg, sig); } }; + // HKDF for TextSecure has a bit of additional handling - salts always end up being 32 bytes Internal.HKDF = function(input, salt, info) { if (salt.byteLength != 32) { @@ -35264,24 +35337,6 @@ var Internal = Internal || {}; }); }; - libsignal.Curve = { - generateKeyPair: function() { - return Internal.crypto.createKeyPair(); - }, - createKeyPair: function(privKey) { - return Internal.crypto.createKeyPair(privKey); - }, - calculateAgreement: function(pubKey, privKey) { - return Internal.crypto.ECDHE(pubKey, privKey); - }, - verifySignature: function(pubKey, msg, sig) { - return Internal.crypto.Ed25519Verify(pubKey, msg, sig); - }, - calculateSignature: function(privKey, message) { - return Internal.crypto.Ed25519Sign(privKey, message); - }, - }; - libsignal.HKDF = { deriveSecrets: function(input, salt, info) { return Internal.HKDF(input, salt, info); @@ -35781,8 +35836,8 @@ Internal.SessionRecord = function() { oldestSession = session; } } - console.log("Deleting session closed at", session.indexInfo.closed); - delete this.sessions[util.toString(oldestBaseKey)]; + console.log("Deleting session closed at", oldestSession.indexInfo.closed); + delete sessions[util.toString(oldestBaseKey)]; } }, }; @@ -36422,7 +36477,7 @@ SessionCipher.prototype = { libsignal.SessionCipher = function(storage, remoteAddress) { var cipher = new SessionCipher(storage, remoteAddress); - // returns a Promise that resolves to a ciphertext array buffer + // returns a Promise that resolves to a ciphertext object this.encrypt = cipher.encrypt.bind(cipher); // returns a Promise that inits a session if necessary and resolves