Update libsignal-protocol v0.9.0
* Exposes crypto APIs. * Move worker methods to libsigna.worker. * Move ProvisioningCipher to libtextsecure.
This commit is contained in:
parent
cd2218ada7
commit
ffa702c934
7 changed files with 293 additions and 238 deletions
|
@ -59,6 +59,7 @@ module.exports = function(grunt) {
|
|||
'libtextsecure/sendmessage.js',
|
||||
'libtextsecure/sync_request.js',
|
||||
'libtextsecure/contacts_parser.js',
|
||||
'libtextsecure/ProvisioningCipher.js',
|
||||
],
|
||||
dest: 'js/libtextsecure.js',
|
||||
},
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
});
|
||||
|
||||
// start a background worker for ecc
|
||||
textsecure.protocol_wrapper.startWorker('/js/libsignal-protocol-worker.js');
|
||||
textsecure.startWorker('/js/libsignal-protocol-worker.js');
|
||||
|
||||
extension.onLaunched(function() {
|
||||
console.log('extension launched');
|
||||
|
|
|
@ -25392,6 +25392,11 @@ Internal.curve25519 = function() {
|
|||
};
|
||||
}();
|
||||
|
||||
;(function() {
|
||||
|
||||
'use strict';
|
||||
window.libsignal = window.libsignal || {};
|
||||
|
||||
var Internal = Internal || {};
|
||||
|
||||
// I am the...workee?
|
||||
|
@ -25401,6 +25406,7 @@ Internal.startWorker = function(url) {
|
|||
Internal.stopWorker(); // there can be only one
|
||||
Internal.curve25519 = new Curve25519Worker(url);
|
||||
};
|
||||
|
||||
Internal.stopWorker = function() {
|
||||
if (Internal.curve25519 instanceof Curve25519Worker) {
|
||||
var worker = Internal.curve25519.worker;
|
||||
|
@ -25409,6 +25415,11 @@ Internal.stopWorker = function() {
|
|||
}
|
||||
};
|
||||
|
||||
libsignal.worker = {
|
||||
startWorker: Internal.startWorker,
|
||||
stopWorker: Internal.stopWorker,
|
||||
};
|
||||
|
||||
function Curve25519Worker(url) {
|
||||
this.jobs = {};
|
||||
this.jobId = 0;
|
||||
|
@ -25447,6 +25458,8 @@ Curve25519Worker.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
/*
|
||||
Copyright 2013 Daniel Wirtz <dcode@dcode.io>
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
@ -34030,7 +34043,7 @@ var Internal = Internal || {};
|
|||
}
|
||||
|
||||
|
||||
var validatePubKeyFormat = function(pubKey) {
|
||||
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) {
|
||||
|
@ -34039,7 +34052,7 @@ var Internal = Internal || {};
|
|||
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) {
|
||||
|
@ -34145,7 +34158,66 @@ var Internal = Internal || {};
|
|||
throw new Error("Got salt of incorrect length");
|
||||
|
||||
return Internal.crypto.HKDF(input, salt, util.toArrayBuffer(info));
|
||||
}
|
||||
};
|
||||
|
||||
Internal.verifyMAC = function(data, key, mac, length) {
|
||||
return Internal.crypto.sign(key, data).then(function(calculated_mac) {
|
||||
if (mac.byteLength != length || calculated_mac.byteLength < length) {
|
||||
throw new Error("Bad MAC length");
|
||||
}
|
||||
var a = new Uint8Array(calculated_mac);
|
||||
var b = new Uint8Array(mac);
|
||||
var result = 0;
|
||||
for (var i=0; i < mac.byteLength; ++i) {
|
||||
result = result | (a[i] ^ b[i]);
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error("Bad MAC");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
libsignal.crypto = {
|
||||
encrypt: function(key, data, iv) {
|
||||
return Internal.crypto.encrypt(key, data, iv);
|
||||
},
|
||||
decrypt: function(key, data, iv) {
|
||||
return Internal.crypto.decrypt(key, data, iv);
|
||||
},
|
||||
calculateMAC: function(key, data) {
|
||||
return Internal.crypto.sign(key, data);
|
||||
},
|
||||
verifyMAC: function(data, key, mac, length) {
|
||||
return Internal.verifyMAC(data, key, mac, length);
|
||||
},
|
||||
getRandomBytes: function(size) {
|
||||
return Internal.crypto.getRandomBytes(size);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
|
@ -34256,105 +34328,6 @@ var util = (function() {
|
|||
};
|
||||
})();
|
||||
|
||||
/* vim: ts=4:sw=4
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
;(function() {
|
||||
|
||||
'use strict';
|
||||
window.libsignal = window.libsignal || {};
|
||||
|
||||
libsignal.protocol = function() {
|
||||
var self = {};
|
||||
|
||||
var verifyMAC = function(data, key, mac, length) {
|
||||
return Internal.crypto.sign(key, data).then(function(calculated_mac) {
|
||||
if (mac.byteLength != length || calculated_mac.byteLength < length) {
|
||||
throw new Error("Bad MAC length");
|
||||
}
|
||||
var a = new Uint8Array(calculated_mac);
|
||||
var b = new Uint8Array(mac);
|
||||
var result = 0;
|
||||
for (var i=0; i < mac.byteLength; ++i) {
|
||||
result = result | (a[i] ^ b[i]);
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error("Bad MAC");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/******************************
|
||||
*** Ratchet implementation ***
|
||||
******************************/
|
||||
self.createIdentityKeyRecvSocket = function() {
|
||||
var socketInfo = {};
|
||||
var keyPair;
|
||||
|
||||
socketInfo.decryptAndHandleDeviceInit = function(deviceInit) {
|
||||
var masterEphemeral = util.toArrayBuffer(deviceInit.publicKey);
|
||||
var message = util.toArrayBuffer(deviceInit.body);
|
||||
|
||||
return Internal.crypto.ECDHE(masterEphemeral, keyPair.privKey).then(function(ecRes) {
|
||||
return Internal.HKDF(ecRes, new ArrayBuffer(32), "TextSecure Provisioning Message").then(function(keys) {
|
||||
if (new Uint8Array(message)[0] != 1)
|
||||
throw new Error("Bad version number on ProvisioningMessage");
|
||||
|
||||
var iv = message.slice(1, 16 + 1);
|
||||
var mac = message.slice(message.byteLength - 32, message.byteLength);
|
||||
var ivAndCiphertext = message.slice(0, message.byteLength - 32);
|
||||
var ciphertext = message.slice(16 + 1, message.byteLength - 32);
|
||||
|
||||
return verifyMAC(ivAndCiphertext, keys[1], mac, 32).then(function() {
|
||||
return Internal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
|
||||
var provisionMessage = Internal.protobuf.ProvisionMessage.decode(plaintext);
|
||||
|
||||
return Internal.crypto.createKeyPair(
|
||||
provisionMessage.identityKeyPrivate.toArrayBuffer()
|
||||
).then(function(identityKeyPair) {
|
||||
return {
|
||||
identityKeyPair : identityKeyPair,
|
||||
number : provisionMessage.number,
|
||||
provisioningCode : provisionMessage.provisioningCode
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Internal.crypto.createKeyPair().then(function(newKeyPair) {
|
||||
keyPair = newKeyPair;
|
||||
socketInfo.pubKey = keyPair.pubKey;
|
||||
return socketInfo;
|
||||
});
|
||||
}
|
||||
|
||||
self.startWorker = function(url) {
|
||||
Internal.startWorker(url);
|
||||
};
|
||||
self.stopWorker = function() {
|
||||
Internal.stopWorker();
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
function isNonNegativeInteger(n) {
|
||||
return (typeof n === 'number' && (n % 1) === 0 && n >= 0);
|
||||
}
|
||||
|
@ -35416,6 +35389,7 @@ Internal.SessionLock.queueJobForNumber = function queueJobForNumber(number, runJ
|
|||
})();
|
||||
|
||||
})();
|
||||
|
||||
/*
|
||||
* vim: ts=4:sw=4:expandtab
|
||||
*/
|
||||
|
@ -35425,20 +35399,10 @@ Internal.SessionLock.queueJobForNumber = function queueJobForNumber(number, runJ
|
|||
window.textsecure.storage = window.textsecure.storage || {};
|
||||
|
||||
textsecure.storage.protocol = new SignalProtocolStore();
|
||||
var protocolInstance = libsignal.protocol(textsecure.storage.protocol);
|
||||
|
||||
window.textsecure = window.textsecure || {};
|
||||
window.textsecure.protocol_wrapper = {
|
||||
startWorker: function(url) {
|
||||
protocolInstance.startWorker(url);
|
||||
},
|
||||
stopWorker: function() {
|
||||
protocolInstance.stopWorker();
|
||||
},
|
||||
createIdentityKeyRecvSocket: function() {
|
||||
return protocolInstance.createIdentityKeyRecvSocket();
|
||||
}
|
||||
};
|
||||
textsecure.ProvisioningCipher = libsignal.ProvisioningCipher;
|
||||
textsecure.startWorker = libsignal.worker.startWorker;
|
||||
textsecure.stopWorker = libsignal.worker.stopWorker;
|
||||
})();
|
||||
|
||||
/*
|
||||
|
@ -36771,7 +36735,8 @@ var TextSecureServer = (function() {
|
|||
var generateKeys = this.generateKeys.bind(this, 100, progressCallback);
|
||||
var registerKeys = this.server.registerKeys.bind(this.server);
|
||||
var getSocket = this.server.getProvisioningSocket.bind(this.server);
|
||||
return textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
|
||||
var provisioningCipher = new libsignal.ProvisioningCipher();
|
||||
return provisioningCipher.getPublicKey().then(function(pubKey) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var socket = getSocket();
|
||||
socket.onclose = function(e) {
|
||||
|
@ -36785,14 +36750,14 @@ var TextSecureServer = (function() {
|
|||
var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
|
||||
setProvisioningUrl([
|
||||
'tsdevice:/?uuid=', proto.uuid, '&pub_key=',
|
||||
encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))
|
||||
encodeURIComponent(btoa(getString(pubKey)))
|
||||
].join(''));
|
||||
request.respond(200, 'OK');
|
||||
} else if (request.path === "/v1/message" && request.verb === "PUT") {
|
||||
var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
|
||||
request.respond(200, 'OK');
|
||||
wsr.close();
|
||||
resolve(cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
|
||||
resolve(provisioningCipher.decrypt(envelope).then(function(provisionMessage) {
|
||||
return confirmNumber(provisionMessage.number).then(function(deviceName) {
|
||||
if (typeof deviceName !== 'string' || deviceName.length === 0) {
|
||||
throw new Error('Invalid device name');
|
||||
|
@ -38162,4 +38127,67 @@ var ContactBuffer = function(arrayBuffer) {
|
|||
};
|
||||
ContactBuffer.prototype = Object.create(ProtoParser.prototype);
|
||||
ContactBuffer.prototype.constructor = ContactBuffer;
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function ProvisioningCipher() {}
|
||||
|
||||
ProvisioningCipher.prototype = {
|
||||
decrypt: function(provisionEnvelope) {
|
||||
var masterEphemeral = provisionEnvelope.publicKey.toArrayBuffer();
|
||||
var message = provisionEnvelope.body.toArrayBuffer();
|
||||
if (new Uint8Array(message)[0] != 1) {
|
||||
throw new Error("Bad version number on ProvisioningMessage");
|
||||
}
|
||||
|
||||
var iv = message.slice(1, 16 + 1);
|
||||
var mac = message.slice(message.byteLength - 32, message.byteLength);
|
||||
var ivAndCiphertext = message.slice(0, message.byteLength - 32);
|
||||
var ciphertext = message.slice(16 + 1, message.byteLength - 32);
|
||||
|
||||
return libsignal.Curve.calculateAgreement(
|
||||
masterEphemeral, this.keyPair.privKey
|
||||
).then(function(ecRes) {
|
||||
return libsignal.HKDF.deriveSecrets(
|
||||
ecRes, new ArrayBuffer(32), "TextSecure Provisioning Message"
|
||||
);
|
||||
}).then(function(keys) {
|
||||
return libsignal.crypto.verifyMAC(ivAndCiphertext, keys[1], mac, 32).then(function() {
|
||||
return libsignal.crypto.decrypt(keys[0], ciphertext, iv);
|
||||
});
|
||||
}).then(function(plaintext) {
|
||||
var provisionMessage = textsecure.protobuf.ProvisionMessage.decode(plaintext);
|
||||
var privKey = provisionMessage.identityKeyPrivate.toArrayBuffer();
|
||||
|
||||
return libsignal.Curve.createKeyPair(privKey).then(function(keyPair) {
|
||||
return {
|
||||
identityKeyPair : keyPair,
|
||||
number : provisionMessage.number,
|
||||
provisioningCode : provisionMessage.provisioningCode
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
getPublicKey: function() {
|
||||
return Promise.resolve().then(function() {
|
||||
if (!this.keyPair) {
|
||||
return libsignal.Curve.generateKeyPair().then(function(keyPair) {
|
||||
this.keyPair = keyPair;
|
||||
}.bind(this));
|
||||
}
|
||||
}.bind(this)).then(function() {
|
||||
return this.keyPair.pubKey;
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
libsignal.ProvisioningCipher = function() {
|
||||
var cipher = new ProvisioningCipher();
|
||||
|
||||
this.decrypt = cipher.decrypt.bind(cipher);
|
||||
this.getPublicKey = cipher.getPublicKey.bind(cipher);
|
||||
};
|
||||
|
||||
})();
|
||||
})();
|
||||
|
|
62
libtextsecure/ProvisioningCipher.js
Normal file
62
libtextsecure/ProvisioningCipher.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
function ProvisioningCipher() {}
|
||||
|
||||
ProvisioningCipher.prototype = {
|
||||
decrypt: function(provisionEnvelope) {
|
||||
var masterEphemeral = provisionEnvelope.publicKey.toArrayBuffer();
|
||||
var message = provisionEnvelope.body.toArrayBuffer();
|
||||
if (new Uint8Array(message)[0] != 1) {
|
||||
throw new Error("Bad version number on ProvisioningMessage");
|
||||
}
|
||||
|
||||
var iv = message.slice(1, 16 + 1);
|
||||
var mac = message.slice(message.byteLength - 32, message.byteLength);
|
||||
var ivAndCiphertext = message.slice(0, message.byteLength - 32);
|
||||
var ciphertext = message.slice(16 + 1, message.byteLength - 32);
|
||||
|
||||
return libsignal.Curve.calculateAgreement(
|
||||
masterEphemeral, this.keyPair.privKey
|
||||
).then(function(ecRes) {
|
||||
return libsignal.HKDF.deriveSecrets(
|
||||
ecRes, new ArrayBuffer(32), "TextSecure Provisioning Message"
|
||||
);
|
||||
}).then(function(keys) {
|
||||
return libsignal.crypto.verifyMAC(ivAndCiphertext, keys[1], mac, 32).then(function() {
|
||||
return libsignal.crypto.decrypt(keys[0], ciphertext, iv);
|
||||
});
|
||||
}).then(function(plaintext) {
|
||||
var provisionMessage = textsecure.protobuf.ProvisionMessage.decode(plaintext);
|
||||
var privKey = provisionMessage.identityKeyPrivate.toArrayBuffer();
|
||||
|
||||
return libsignal.Curve.createKeyPair(privKey).then(function(keyPair) {
|
||||
return {
|
||||
identityKeyPair : keyPair,
|
||||
number : provisionMessage.number,
|
||||
provisioningCode : provisionMessage.provisioningCode
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
getPublicKey: function() {
|
||||
return Promise.resolve().then(function() {
|
||||
if (!this.keyPair) {
|
||||
return libsignal.Curve.generateKeyPair().then(function(keyPair) {
|
||||
this.keyPair = keyPair;
|
||||
}.bind(this));
|
||||
}
|
||||
}.bind(this)).then(function() {
|
||||
return this.keyPair.pubKey;
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
libsignal.ProvisioningCipher = function() {
|
||||
var cipher = new ProvisioningCipher();
|
||||
|
||||
this.decrypt = cipher.decrypt.bind(cipher);
|
||||
this.getPublicKey = cipher.getPublicKey.bind(cipher);
|
||||
};
|
||||
|
||||
})();
|
|
@ -35,7 +35,8 @@
|
|||
var generateKeys = this.generateKeys.bind(this, 100, progressCallback);
|
||||
var registerKeys = this.server.registerKeys.bind(this.server);
|
||||
var getSocket = this.server.getProvisioningSocket.bind(this.server);
|
||||
return textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
|
||||
var provisioningCipher = new libsignal.ProvisioningCipher();
|
||||
return provisioningCipher.getPublicKey().then(function(pubKey) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var socket = getSocket();
|
||||
socket.onclose = function(e) {
|
||||
|
@ -49,14 +50,14 @@
|
|||
var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
|
||||
setProvisioningUrl([
|
||||
'tsdevice:/?uuid=', proto.uuid, '&pub_key=',
|
||||
encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))
|
||||
encodeURIComponent(btoa(getString(pubKey)))
|
||||
].join(''));
|
||||
request.respond(200, 'OK');
|
||||
} else if (request.path === "/v1/message" && request.verb === "PUT") {
|
||||
var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
|
||||
request.respond(200, 'OK');
|
||||
wsr.close();
|
||||
resolve(cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
|
||||
resolve(provisioningCipher.decrypt(envelope).then(function(provisionMessage) {
|
||||
return confirmNumber(provisionMessage.number).then(function(deviceName) {
|
||||
if (typeof deviceName !== 'string' || deviceName.length === 0) {
|
||||
throw new Error('Invalid device name');
|
||||
|
|
|
@ -25278,6 +25278,11 @@ Internal.curve25519 = function() {
|
|||
};
|
||||
}();
|
||||
|
||||
;(function() {
|
||||
|
||||
'use strict';
|
||||
window.libsignal = window.libsignal || {};
|
||||
|
||||
var Internal = Internal || {};
|
||||
|
||||
// I am the...workee?
|
||||
|
@ -25287,6 +25292,7 @@ Internal.startWorker = function(url) {
|
|||
Internal.stopWorker(); // there can be only one
|
||||
Internal.curve25519 = new Curve25519Worker(url);
|
||||
};
|
||||
|
||||
Internal.stopWorker = function() {
|
||||
if (Internal.curve25519 instanceof Curve25519Worker) {
|
||||
var worker = Internal.curve25519.worker;
|
||||
|
@ -25295,6 +25301,11 @@ Internal.stopWorker = function() {
|
|||
}
|
||||
};
|
||||
|
||||
libsignal.worker = {
|
||||
startWorker: Internal.startWorker,
|
||||
stopWorker: Internal.stopWorker,
|
||||
};
|
||||
|
||||
function Curve25519Worker(url) {
|
||||
this.jobs = {};
|
||||
this.jobId = 0;
|
||||
|
@ -25333,6 +25344,8 @@ Curve25519Worker.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
/*
|
||||
Copyright 2013 Daniel Wirtz <dcode@dcode.io>
|
||||
Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
@ -33916,7 +33929,7 @@ var Internal = Internal || {};
|
|||
}
|
||||
|
||||
|
||||
var validatePubKeyFormat = function(pubKey) {
|
||||
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) {
|
||||
|
@ -33925,7 +33938,7 @@ var Internal = Internal || {};
|
|||
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) {
|
||||
|
@ -34031,7 +34044,66 @@ var Internal = Internal || {};
|
|||
throw new Error("Got salt of incorrect length");
|
||||
|
||||
return Internal.crypto.HKDF(input, salt, util.toArrayBuffer(info));
|
||||
}
|
||||
};
|
||||
|
||||
Internal.verifyMAC = function(data, key, mac, length) {
|
||||
return Internal.crypto.sign(key, data).then(function(calculated_mac) {
|
||||
if (mac.byteLength != length || calculated_mac.byteLength < length) {
|
||||
throw new Error("Bad MAC length");
|
||||
}
|
||||
var a = new Uint8Array(calculated_mac);
|
||||
var b = new Uint8Array(mac);
|
||||
var result = 0;
|
||||
for (var i=0; i < mac.byteLength; ++i) {
|
||||
result = result | (a[i] ^ b[i]);
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error("Bad MAC");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
libsignal.crypto = {
|
||||
encrypt: function(key, data, iv) {
|
||||
return Internal.crypto.encrypt(key, data, iv);
|
||||
},
|
||||
decrypt: function(key, data, iv) {
|
||||
return Internal.crypto.decrypt(key, data, iv);
|
||||
},
|
||||
calculateMAC: function(key, data) {
|
||||
return Internal.crypto.sign(key, data);
|
||||
},
|
||||
verifyMAC: function(data, key, mac, length) {
|
||||
return Internal.verifyMAC(data, key, mac, length);
|
||||
},
|
||||
getRandomBytes: function(size) {
|
||||
return Internal.crypto.getRandomBytes(size);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
|
@ -34142,105 +34214,6 @@ var util = (function() {
|
|||
};
|
||||
})();
|
||||
|
||||
/* vim: ts=4:sw=4
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
;(function() {
|
||||
|
||||
'use strict';
|
||||
window.libsignal = window.libsignal || {};
|
||||
|
||||
libsignal.protocol = function() {
|
||||
var self = {};
|
||||
|
||||
var verifyMAC = function(data, key, mac, length) {
|
||||
return Internal.crypto.sign(key, data).then(function(calculated_mac) {
|
||||
if (mac.byteLength != length || calculated_mac.byteLength < length) {
|
||||
throw new Error("Bad MAC length");
|
||||
}
|
||||
var a = new Uint8Array(calculated_mac);
|
||||
var b = new Uint8Array(mac);
|
||||
var result = 0;
|
||||
for (var i=0; i < mac.byteLength; ++i) {
|
||||
result = result | (a[i] ^ b[i]);
|
||||
}
|
||||
if (result !== 0) {
|
||||
throw new Error("Bad MAC");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/******************************
|
||||
*** Ratchet implementation ***
|
||||
******************************/
|
||||
self.createIdentityKeyRecvSocket = function() {
|
||||
var socketInfo = {};
|
||||
var keyPair;
|
||||
|
||||
socketInfo.decryptAndHandleDeviceInit = function(deviceInit) {
|
||||
var masterEphemeral = util.toArrayBuffer(deviceInit.publicKey);
|
||||
var message = util.toArrayBuffer(deviceInit.body);
|
||||
|
||||
return Internal.crypto.ECDHE(masterEphemeral, keyPair.privKey).then(function(ecRes) {
|
||||
return Internal.HKDF(ecRes, new ArrayBuffer(32), "TextSecure Provisioning Message").then(function(keys) {
|
||||
if (new Uint8Array(message)[0] != 1)
|
||||
throw new Error("Bad version number on ProvisioningMessage");
|
||||
|
||||
var iv = message.slice(1, 16 + 1);
|
||||
var mac = message.slice(message.byteLength - 32, message.byteLength);
|
||||
var ivAndCiphertext = message.slice(0, message.byteLength - 32);
|
||||
var ciphertext = message.slice(16 + 1, message.byteLength - 32);
|
||||
|
||||
return verifyMAC(ivAndCiphertext, keys[1], mac, 32).then(function() {
|
||||
return Internal.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
|
||||
var provisionMessage = Internal.protobuf.ProvisionMessage.decode(plaintext);
|
||||
|
||||
return Internal.crypto.createKeyPair(
|
||||
provisionMessage.identityKeyPrivate.toArrayBuffer()
|
||||
).then(function(identityKeyPair) {
|
||||
return {
|
||||
identityKeyPair : identityKeyPair,
|
||||
number : provisionMessage.number,
|
||||
provisioningCode : provisionMessage.provisioningCode
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Internal.crypto.createKeyPair().then(function(newKeyPair) {
|
||||
keyPair = newKeyPair;
|
||||
socketInfo.pubKey = keyPair.pubKey;
|
||||
return socketInfo;
|
||||
});
|
||||
}
|
||||
|
||||
self.startWorker = function(url) {
|
||||
Internal.startWorker(url);
|
||||
};
|
||||
self.stopWorker = function() {
|
||||
Internal.stopWorker();
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
function isNonNegativeInteger(n) {
|
||||
return (typeof n === 'number' && (n % 1) === 0 && n >= 0);
|
||||
}
|
||||
|
@ -35301,4 +35274,4 @@ Internal.SessionLock.queueJobForNumber = function queueJobForNumber(number, runJ
|
|||
|
||||
})();
|
||||
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -7,18 +7,8 @@
|
|||
window.textsecure.storage = window.textsecure.storage || {};
|
||||
|
||||
textsecure.storage.protocol = new SignalProtocolStore();
|
||||
var protocolInstance = libsignal.protocol(textsecure.storage.protocol);
|
||||
|
||||
window.textsecure = window.textsecure || {};
|
||||
window.textsecure.protocol_wrapper = {
|
||||
startWorker: function(url) {
|
||||
protocolInstance.startWorker(url);
|
||||
},
|
||||
stopWorker: function() {
|
||||
protocolInstance.stopWorker();
|
||||
},
|
||||
createIdentityKeyRecvSocket: function() {
|
||||
return protocolInstance.createIdentityKeyRecvSocket();
|
||||
}
|
||||
};
|
||||
textsecure.ProvisioningCipher = libsignal.ProvisioningCipher;
|
||||
textsecure.startWorker = libsignal.worker.startWorker;
|
||||
textsecure.stopWorker = libsignal.worker.stopWorker;
|
||||
})();
|
||||
|
|
Loading…
Reference in a new issue