Compare commits

..

No commits in common. "master" and "v1.1.6" have entirely different histories.

15 changed files with 418 additions and 709 deletions

View File

@ -1,10 +1,10 @@
language: node_js
node_js:
- '6'
- '0.10'
install:
- npm install
- npm install
script:
- npm test
- npm test
env:
global:
- secure: rdRRKiD0Ndfcy66gFOJP/8FZhx9bVk1VDUnMnavyLMbOKKV2jH4qkoFlYqr73D/EDADL8QepsKdGc51VtYewED2Ki98KNv+CmoXPfURqj97mAH3HycV4soinqC46elEpcIbhoOL2skyBRv8F2VwRyWzQiHKTlXrkL4anxzfF0xyVAo07Sy010AOgCLHU+j+kg1VTSwKtUSHJY5zunX+oNDJVQTPGKtWJ4eODggLv4eSRI8+fu9Z/AA+tV5BBU1d7Ey3GY9TD0Ady4HMZmvGGIQ76M6ULb++cXlAOGLR9Vv4YeCS1TnhLoZQGEJ+UFRR1hpVF4m3ignhqSkJ5+lvpFQAAKg0gS0GwNseR2XFNGusLwyUazQRk7u00s808lJ5GutL+rLGM2eZCAZprwe2SMxrAW5GLm6u/YMZycxGyBA/ikP11X9v5Zx1m6bNRJdRmfRiPFD5Il+Q29i1DWVVawvjkrcRH9GPtNxES64jS0K7hU28DQYq2vgWEP5AY6Uwsz2bK0wEswfffk4VDFps73cx39EMRH3xP4PpIRxsGddfYejcBH+9rGH+eaHzpQ8Q3zNsn7muYjZXcVPIu6Pze5mUmsox++Kq2llTXTy/WJ6MeDv2E/6AE7ArXs61E1lKDb+vcciWRBuD9gy5SaFQ6RJ/3cn4QLCWXNw4TUabeYrE=

View File

@ -1,12 +1,7 @@
**Deprecation Warning**: It is recommended that the TypeScript interface of [libsignal-client](https://github.com/signalapp/libsignal-client) be used for all new applications. This library is no longer used by us or maintained.
# libsignal-protocol-javascript
[![Build Status](https://travis-ci.org/signalapp/libsignal-protocol-javascript.svg?branch=master)](https://travis-ci.org/signalapp/libsignal-protocol-javascript)
#libsignal-protocol-javascript
Signal Protocol implementation for the browser based on
[libsignal-protocol-java](https://github.com/signalapp/libsignal-protocol-java).
[libsignal-protocol-java](https://github.com/WhisperSystems/libsignal-protocol-java).
```
/dist # Distributables
@ -117,7 +112,7 @@ var store = new MySignalProtocolStore();
var address = new libsignal.SignalProtocolAddress(recipientId, deviceId);
// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
var sessionBuilder = new libsignal.SessionBuilder(store, address);
SessionBuilder 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
@ -186,7 +181,7 @@ sessionCipher.decryptWhisperMessage(ciphertext).then(function(plaintext) {
## Building
To compile curve25519 from C source files in `/native`, install
To compile curve25519 from C souce files in `/native`, install
[emscripten](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
```
@ -195,6 +190,6 @@ grunt compile
## License
Copyright 2015-2018 Open Whisper Systems
Copyright 2015-2016 Open Whisper Systems
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html

View File

@ -47,18 +47,21 @@
/**
* The low 32 bits as a signed value.
* @type {number}
* @expose
*/
this.low = low | 0;
/**
* The high 32 bits as a signed value.
* @type {number}
* @expose
*/
this.high = high | 0;
/**
* Whether unsigned or not.
* @type {boolean}
* @expose
*/
this.unsigned = !!unsigned;
}
@ -84,9 +87,10 @@
* An indicator used to reliably determine if an object is a Long or not.
* @type {boolean}
* @const
* @expose
* @private
*/
Long.prototype.__isLong__;
Long.__isLong__;
Object.defineProperty(Long.prototype, "__isLong__", {
value: true,
@ -109,6 +113,7 @@
* @function
* @param {*} obj Object
* @returns {boolean}
* @expose
*/
Long.isLong = isLong;
@ -165,6 +170,7 @@
* @param {number} value The 32 bit integer in question
* @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromInt = fromInt;
@ -199,6 +205,7 @@
* @param {number} value The number in question
* @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromNumber = fromNumber;
@ -221,6 +228,7 @@
* @param {number} highBits The high 32 bits
* @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromBits = fromBits;
@ -290,6 +298,7 @@
* @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed
* @param {number=} radix The radix in which the text is written (2-36), defaults to 10
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromString = fromString;
@ -315,6 +324,7 @@
* @function
* @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value
* @returns {!Long}
* @expose
*/
Long.fromValue = fromValue;
@ -372,6 +382,7 @@
/**
* Signed zero.
* @type {!Long}
* @expose
*/
Long.ZERO = ZERO;
@ -384,6 +395,7 @@
/**
* Unsigned zero.
* @type {!Long}
* @expose
*/
Long.UZERO = UZERO;
@ -396,6 +408,7 @@
/**
* Signed one.
* @type {!Long}
* @expose
*/
Long.ONE = ONE;
@ -408,6 +421,7 @@
/**
* Unsigned one.
* @type {!Long}
* @expose
*/
Long.UONE = UONE;
@ -420,6 +434,7 @@
/**
* Signed negative one.
* @type {!Long}
* @expose
*/
Long.NEG_ONE = NEG_ONE;
@ -432,6 +447,7 @@
/**
* Maximum signed value.
* @type {!Long}
* @expose
*/
Long.MAX_VALUE = MAX_VALUE;
@ -444,6 +460,7 @@
/**
* Maximum unsigned value.
* @type {!Long}
* @expose
*/
Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE;
@ -456,6 +473,7 @@
/**
* Minimum signed value.
* @type {!Long}
* @expose
*/
Long.MIN_VALUE = MIN_VALUE;
@ -468,6 +486,7 @@
/**
* Converts the Long to a 32 bit integer, assuming it is a 32 bit integer.
* @returns {number}
* @expose
*/
LongPrototype.toInt = function toInt() {
return this.unsigned ? this.low >>> 0 : this.low;
@ -476,6 +495,7 @@
/**
* Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa).
* @returns {number}
* @expose
*/
LongPrototype.toNumber = function toNumber() {
if (this.unsigned)
@ -489,6 +509,7 @@
* @returns {string}
* @override
* @throws {RangeError} If `radix` is out of range
* @expose
*/
LongPrototype.toString = function toString(radix) {
radix = radix || 10;
@ -531,6 +552,7 @@
/**
* Gets the high 32 bits as a signed integer.
* @returns {number} Signed high bits
* @expose
*/
LongPrototype.getHighBits = function getHighBits() {
return this.high;
@ -539,6 +561,7 @@
/**
* Gets the high 32 bits as an unsigned integer.
* @returns {number} Unsigned high bits
* @expose
*/
LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() {
return this.high >>> 0;
@ -547,6 +570,7 @@
/**
* Gets the low 32 bits as a signed integer.
* @returns {number} Signed low bits
* @expose
*/
LongPrototype.getLowBits = function getLowBits() {
return this.low;
@ -555,6 +579,7 @@
/**
* Gets the low 32 bits as an unsigned integer.
* @returns {number} Unsigned low bits
* @expose
*/
LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() {
return this.low >>> 0;
@ -563,6 +588,7 @@
/**
* Gets the number of bits needed to represent the absolute value of this Long.
* @returns {number}
* @expose
*/
LongPrototype.getNumBitsAbs = function getNumBitsAbs() {
if (this.isNegative()) // Unsigned Longs are never negative
@ -577,6 +603,7 @@
/**
* Tests if this Long's value equals zero.
* @returns {boolean}
* @expose
*/
LongPrototype.isZero = function isZero() {
return this.high === 0 && this.low === 0;
@ -585,6 +612,7 @@
/**
* Tests if this Long's value is negative.
* @returns {boolean}
* @expose
*/
LongPrototype.isNegative = function isNegative() {
return !this.unsigned && this.high < 0;
@ -593,6 +621,7 @@
/**
* Tests if this Long's value is positive.
* @returns {boolean}
* @expose
*/
LongPrototype.isPositive = function isPositive() {
return this.unsigned || this.high >= 0;
@ -601,6 +630,7 @@
/**
* Tests if this Long's value is odd.
* @returns {boolean}
* @expose
*/
LongPrototype.isOdd = function isOdd() {
return (this.low & 1) === 1;
@ -609,6 +639,7 @@
/**
* Tests if this Long's value is even.
* @returns {boolean}
* @expose
*/
LongPrototype.isEven = function isEven() {
return (this.low & 1) === 0;
@ -618,6 +649,7 @@
* Tests if this Long's value equals the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.equals = function equals(other) {
if (!isLong(other))
@ -632,6 +664,7 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.eq = LongPrototype.equals;
@ -639,6 +672,7 @@
* Tests if this Long's value differs from the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.notEquals = function notEquals(other) {
return !this.eq(/* validates */ other);
@ -649,6 +683,7 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.neq = LongPrototype.notEquals;
@ -656,6 +691,7 @@
* Tests if this Long's value is less than the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lessThan = function lessThan(other) {
return this.comp(/* validates */ other) < 0;
@ -666,6 +702,7 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lt = LongPrototype.lessThan;
@ -673,6 +710,7 @@
* Tests if this Long's value is less than or equal the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) {
return this.comp(/* validates */ other) <= 0;
@ -683,6 +721,7 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lte = LongPrototype.lessThanOrEqual;
@ -690,6 +729,7 @@
* Tests if this Long's value is greater than the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.greaterThan = function greaterThan(other) {
return this.comp(/* validates */ other) > 0;
@ -700,6 +740,7 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gt = LongPrototype.greaterThan;
@ -707,6 +748,7 @@
* Tests if this Long's value is greater than or equal the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) {
return this.comp(/* validates */ other) >= 0;
@ -717,6 +759,7 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gte = LongPrototype.greaterThanOrEqual;
@ -725,6 +768,7 @@
* @param {!Long|number|string} other Other value
* @returns {number} 0 if they are the same, 1 if the this is greater and -1
* if the given one is greater
* @expose
*/
LongPrototype.compare = function compare(other) {
if (!isLong(other))
@ -750,12 +794,14 @@
* @param {!Long|number|string} other Other value
* @returns {number} 0 if they are the same, 1 if the this is greater and -1
* if the given one is greater
* @expose
*/
LongPrototype.comp = LongPrototype.compare;
/**
* Negates this Long's value.
* @returns {!Long} Negated Long
* @expose
*/
LongPrototype.negate = function negate() {
if (!this.unsigned && this.eq(MIN_VALUE))
@ -767,6 +813,7 @@
* Negates this Long's value. This is an alias of {@link Long#negate}.
* @function
* @returns {!Long} Negated Long
* @expose
*/
LongPrototype.neg = LongPrototype.negate;
@ -774,6 +821,7 @@
* Returns the sum of this and the specified Long.
* @param {!Long|number|string} addend Addend
* @returns {!Long} Sum
* @expose
*/
LongPrototype.add = function add(addend) {
if (!isLong(addend))
@ -810,6 +858,7 @@
* Returns the difference of this and the specified Long.
* @param {!Long|number|string} subtrahend Subtrahend
* @returns {!Long} Difference
* @expose
*/
LongPrototype.subtract = function subtract(subtrahend) {
if (!isLong(subtrahend))
@ -822,6 +871,7 @@
* @function
* @param {!Long|number|string} subtrahend Subtrahend
* @returns {!Long} Difference
* @expose
*/
LongPrototype.sub = LongPrototype.subtract;
@ -829,6 +879,7 @@
* Returns the product of this and the specified Long.
* @param {!Long|number|string} multiplier Multiplier
* @returns {!Long} Product
* @expose
*/
LongPrototype.multiply = function multiply(multiplier) {
if (this.isZero())
@ -896,6 +947,7 @@
* @function
* @param {!Long|number|string} multiplier Multiplier
* @returns {!Long} Product
* @expose
*/
LongPrototype.mul = LongPrototype.multiply;
@ -904,6 +956,7 @@
* unsigned if this Long is unsigned.
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.divide = function divide(divisor) {
if (!isLong(divisor))
@ -914,8 +967,6 @@
return this.unsigned ? UZERO : ZERO;
var approx, rem, res;
if (!this.unsigned) {
// This section is only relevant for signed longs and is derived from the
// closure library as a whole.
if (this.eq(MIN_VALUE)) {
if (divisor.eq(ONE) || divisor.eq(NEG_ONE))
return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
@ -941,18 +992,19 @@
return this.neg().div(divisor).neg();
} else if (divisor.isNegative())
return this.div(divisor.neg()).neg();
res = ZERO;
} else {
// The algorithm below has not been made for unsigned longs. It's therefore
// required to take special care of the MSB prior to running it.
if (!divisor.unsigned)
divisor = divisor.toUnsigned();
} else if (!divisor.unsigned)
divisor = divisor.toUnsigned();
// The algorithm below has not been made for unsigned longs. It's therefore
// required to take special care of the MSB prior to running it.
if (this.unsigned) {
if (divisor.gt(this))
return UZERO;
if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true
return UONE;
res = UZERO;
}
} else
res = ZERO;
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
@ -996,6 +1048,7 @@
* @function
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.div = LongPrototype.divide;
@ -1003,6 +1056,7 @@
* Returns this Long modulo the specified.
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Remainder
* @expose
*/
LongPrototype.modulo = function modulo(divisor) {
if (!isLong(divisor))
@ -1015,12 +1069,14 @@
* @function
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Remainder
* @expose
*/
LongPrototype.mod = LongPrototype.modulo;
/**
* Returns the bitwise NOT of this Long.
* @returns {!Long}
* @expose
*/
LongPrototype.not = function not() {
return fromBits(~this.low, ~this.high, this.unsigned);
@ -1030,6 +1086,7 @@
* Returns the bitwise AND of this Long and the specified.
* @param {!Long|number|string} other Other Long
* @returns {!Long}
* @expose
*/
LongPrototype.and = function and(other) {
if (!isLong(other))
@ -1041,6 +1098,7 @@
* Returns the bitwise OR of this Long and the specified.
* @param {!Long|number|string} other Other Long
* @returns {!Long}
* @expose
*/
LongPrototype.or = function or(other) {
if (!isLong(other))
@ -1052,6 +1110,7 @@
* Returns the bitwise XOR of this Long and the given one.
* @param {!Long|number|string} other Other Long
* @returns {!Long}
* @expose
*/
LongPrototype.xor = function xor(other) {
if (!isLong(other))
@ -1063,6 +1122,7 @@
* Returns this Long with bits shifted to the left by the given amount.
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shiftLeft = function shiftLeft(numBits) {
if (isLong(numBits))
@ -1080,6 +1140,7 @@
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shl = LongPrototype.shiftLeft;
@ -1087,6 +1148,7 @@
* Returns this Long with bits arithmetically shifted to the right by the given amount.
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shiftRight = function shiftRight(numBits) {
if (isLong(numBits))
@ -1104,6 +1166,7 @@
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shr = LongPrototype.shiftRight;
@ -1111,6 +1174,7 @@
* Returns this Long with bits logically shifted to the right by the given amount.
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) {
if (isLong(numBits))
@ -1135,12 +1199,14 @@
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shru = LongPrototype.shiftRightUnsigned;
/**
* Converts this Long to signed.
* @returns {!Long} Signed long
* @expose
*/
LongPrototype.toSigned = function toSigned() {
if (!this.unsigned)
@ -1151,6 +1217,7 @@
/**
* Converts this Long to unsigned.
* @returns {!Long} Unsigned long
* @expose
*/
LongPrototype.toUnsigned = function toUnsigned() {
if (this.unsigned)
@ -1158,53 +1225,6 @@
return fromBits(this.low, this.high, true);
};
/**
* Converts this Long to its byte representation.
* @param {boolean=} le Whether little or big endian, defaults to big endian
* @returns {!Array.<number>} Byte representation
*/
LongPrototype.toBytes = function(le) {
return le ? this.toBytesLE() : this.toBytesBE();
}
/**
* Converts this Long to its little endian byte representation.
* @returns {!Array.<number>} Little endian byte representation
*/
LongPrototype.toBytesLE = function() {
var hi = this.high,
lo = this.low;
return [
lo & 0xff,
(lo >>> 8) & 0xff,
(lo >>> 16) & 0xff,
(lo >>> 24) & 0xff,
hi & 0xff,
(hi >>> 8) & 0xff,
(hi >>> 16) & 0xff,
(hi >>> 24) & 0xff
];
}
/**
* Converts this Long to its big endian byte representation.
* @returns {!Array.<number>} Big endian byte representation
*/
LongPrototype.toBytesBE = function() {
var hi = this.high,
lo = this.low;
return [
(hi >>> 24) & 0xff,
(hi >>> 16) & 0xff,
(hi >>> 8) & 0xff,
hi & 0xff,
(lo >>> 24) & 0xff,
(lo >>> 16) & 0xff,
(lo >>> 8) & 0xff,
lo & 0xff
];
}
return Long;
});

View File

@ -25399,18 +25399,21 @@ Curve25519Worker.prototype = {
/**
* The low 32 bits as a signed value.
* @type {number}
* @expose
*/
this.low = low | 0;
/**
* The high 32 bits as a signed value.
* @type {number}
* @expose
*/
this.high = high | 0;
/**
* Whether unsigned or not.
* @type {boolean}
* @expose
*/
this.unsigned = !!unsigned;
}
@ -25436,9 +25439,10 @@ Curve25519Worker.prototype = {
* An indicator used to reliably determine if an object is a Long or not.
* @type {boolean}
* @const
* @expose
* @private
*/
Long.prototype.__isLong__;
Long.__isLong__;
Object.defineProperty(Long.prototype, "__isLong__", {
value: true,
@ -25461,6 +25465,7 @@ Curve25519Worker.prototype = {
* @function
* @param {*} obj Object
* @returns {boolean}
* @expose
*/
Long.isLong = isLong;
@ -25517,6 +25522,7 @@ Curve25519Worker.prototype = {
* @param {number} value The 32 bit integer in question
* @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromInt = fromInt;
@ -25551,6 +25557,7 @@ Curve25519Worker.prototype = {
* @param {number} value The number in question
* @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromNumber = fromNumber;
@ -25573,6 +25580,7 @@ Curve25519Worker.prototype = {
* @param {number} highBits The high 32 bits
* @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromBits = fromBits;
@ -25642,6 +25650,7 @@ Curve25519Worker.prototype = {
* @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed
* @param {number=} radix The radix in which the text is written (2-36), defaults to 10
* @returns {!Long} The corresponding Long value
* @expose
*/
Long.fromString = fromString;
@ -25667,6 +25676,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value
* @returns {!Long}
* @expose
*/
Long.fromValue = fromValue;
@ -25724,6 +25734,7 @@ Curve25519Worker.prototype = {
/**
* Signed zero.
* @type {!Long}
* @expose
*/
Long.ZERO = ZERO;
@ -25736,6 +25747,7 @@ Curve25519Worker.prototype = {
/**
* Unsigned zero.
* @type {!Long}
* @expose
*/
Long.UZERO = UZERO;
@ -25748,6 +25760,7 @@ Curve25519Worker.prototype = {
/**
* Signed one.
* @type {!Long}
* @expose
*/
Long.ONE = ONE;
@ -25760,6 +25773,7 @@ Curve25519Worker.prototype = {
/**
* Unsigned one.
* @type {!Long}
* @expose
*/
Long.UONE = UONE;
@ -25772,6 +25786,7 @@ Curve25519Worker.prototype = {
/**
* Signed negative one.
* @type {!Long}
* @expose
*/
Long.NEG_ONE = NEG_ONE;
@ -25784,6 +25799,7 @@ Curve25519Worker.prototype = {
/**
* Maximum signed value.
* @type {!Long}
* @expose
*/
Long.MAX_VALUE = MAX_VALUE;
@ -25796,6 +25812,7 @@ Curve25519Worker.prototype = {
/**
* Maximum unsigned value.
* @type {!Long}
* @expose
*/
Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE;
@ -25808,6 +25825,7 @@ Curve25519Worker.prototype = {
/**
* Minimum signed value.
* @type {!Long}
* @expose
*/
Long.MIN_VALUE = MIN_VALUE;
@ -25820,6 +25838,7 @@ Curve25519Worker.prototype = {
/**
* Converts the Long to a 32 bit integer, assuming it is a 32 bit integer.
* @returns {number}
* @expose
*/
LongPrototype.toInt = function toInt() {
return this.unsigned ? this.low >>> 0 : this.low;
@ -25828,6 +25847,7 @@ Curve25519Worker.prototype = {
/**
* Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa).
* @returns {number}
* @expose
*/
LongPrototype.toNumber = function toNumber() {
if (this.unsigned)
@ -25841,6 +25861,7 @@ Curve25519Worker.prototype = {
* @returns {string}
* @override
* @throws {RangeError} If `radix` is out of range
* @expose
*/
LongPrototype.toString = function toString(radix) {
radix = radix || 10;
@ -25883,6 +25904,7 @@ Curve25519Worker.prototype = {
/**
* Gets the high 32 bits as a signed integer.
* @returns {number} Signed high bits
* @expose
*/
LongPrototype.getHighBits = function getHighBits() {
return this.high;
@ -25891,6 +25913,7 @@ Curve25519Worker.prototype = {
/**
* Gets the high 32 bits as an unsigned integer.
* @returns {number} Unsigned high bits
* @expose
*/
LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() {
return this.high >>> 0;
@ -25899,6 +25922,7 @@ Curve25519Worker.prototype = {
/**
* Gets the low 32 bits as a signed integer.
* @returns {number} Signed low bits
* @expose
*/
LongPrototype.getLowBits = function getLowBits() {
return this.low;
@ -25907,6 +25931,7 @@ Curve25519Worker.prototype = {
/**
* Gets the low 32 bits as an unsigned integer.
* @returns {number} Unsigned low bits
* @expose
*/
LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() {
return this.low >>> 0;
@ -25915,6 +25940,7 @@ Curve25519Worker.prototype = {
/**
* Gets the number of bits needed to represent the absolute value of this Long.
* @returns {number}
* @expose
*/
LongPrototype.getNumBitsAbs = function getNumBitsAbs() {
if (this.isNegative()) // Unsigned Longs are never negative
@ -25929,6 +25955,7 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value equals zero.
* @returns {boolean}
* @expose
*/
LongPrototype.isZero = function isZero() {
return this.high === 0 && this.low === 0;
@ -25937,6 +25964,7 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is negative.
* @returns {boolean}
* @expose
*/
LongPrototype.isNegative = function isNegative() {
return !this.unsigned && this.high < 0;
@ -25945,6 +25973,7 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is positive.
* @returns {boolean}
* @expose
*/
LongPrototype.isPositive = function isPositive() {
return this.unsigned || this.high >= 0;
@ -25953,6 +25982,7 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is odd.
* @returns {boolean}
* @expose
*/
LongPrototype.isOdd = function isOdd() {
return (this.low & 1) === 1;
@ -25961,6 +25991,7 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is even.
* @returns {boolean}
* @expose
*/
LongPrototype.isEven = function isEven() {
return (this.low & 1) === 0;
@ -25970,6 +26001,7 @@ Curve25519Worker.prototype = {
* Tests if this Long's value equals the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.equals = function equals(other) {
if (!isLong(other))
@ -25984,6 +26016,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.eq = LongPrototype.equals;
@ -25991,6 +26024,7 @@ Curve25519Worker.prototype = {
* Tests if this Long's value differs from the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.notEquals = function notEquals(other) {
return !this.eq(/* validates */ other);
@ -26001,6 +26035,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.neq = LongPrototype.notEquals;
@ -26008,6 +26043,7 @@ Curve25519Worker.prototype = {
* Tests if this Long's value is less than the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lessThan = function lessThan(other) {
return this.comp(/* validates */ other) < 0;
@ -26018,6 +26054,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lt = LongPrototype.lessThan;
@ -26025,6 +26062,7 @@ Curve25519Worker.prototype = {
* Tests if this Long's value is less than or equal the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) {
return this.comp(/* validates */ other) <= 0;
@ -26035,6 +26073,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lte = LongPrototype.lessThanOrEqual;
@ -26042,6 +26081,7 @@ Curve25519Worker.prototype = {
* Tests if this Long's value is greater than the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.greaterThan = function greaterThan(other) {
return this.comp(/* validates */ other) > 0;
@ -26052,6 +26092,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gt = LongPrototype.greaterThan;
@ -26059,6 +26100,7 @@ Curve25519Worker.prototype = {
* Tests if this Long's value is greater than or equal the specified's.
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) {
return this.comp(/* validates */ other) >= 0;
@ -26069,6 +26111,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gte = LongPrototype.greaterThanOrEqual;
@ -26077,6 +26120,7 @@ Curve25519Worker.prototype = {
* @param {!Long|number|string} other Other value
* @returns {number} 0 if they are the same, 1 if the this is greater and -1
* if the given one is greater
* @expose
*/
LongPrototype.compare = function compare(other) {
if (!isLong(other))
@ -26102,12 +26146,14 @@ Curve25519Worker.prototype = {
* @param {!Long|number|string} other Other value
* @returns {number} 0 if they are the same, 1 if the this is greater and -1
* if the given one is greater
* @expose
*/
LongPrototype.comp = LongPrototype.compare;
/**
* Negates this Long's value.
* @returns {!Long} Negated Long
* @expose
*/
LongPrototype.negate = function negate() {
if (!this.unsigned && this.eq(MIN_VALUE))
@ -26119,6 +26165,7 @@ Curve25519Worker.prototype = {
* Negates this Long's value. This is an alias of {@link Long#negate}.
* @function
* @returns {!Long} Negated Long
* @expose
*/
LongPrototype.neg = LongPrototype.negate;
@ -26126,6 +26173,7 @@ Curve25519Worker.prototype = {
* Returns the sum of this and the specified Long.
* @param {!Long|number|string} addend Addend
* @returns {!Long} Sum
* @expose
*/
LongPrototype.add = function add(addend) {
if (!isLong(addend))
@ -26162,6 +26210,7 @@ Curve25519Worker.prototype = {
* Returns the difference of this and the specified Long.
* @param {!Long|number|string} subtrahend Subtrahend
* @returns {!Long} Difference
* @expose
*/
LongPrototype.subtract = function subtract(subtrahend) {
if (!isLong(subtrahend))
@ -26174,6 +26223,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} subtrahend Subtrahend
* @returns {!Long} Difference
* @expose
*/
LongPrototype.sub = LongPrototype.subtract;
@ -26181,6 +26231,7 @@ Curve25519Worker.prototype = {
* Returns the product of this and the specified Long.
* @param {!Long|number|string} multiplier Multiplier
* @returns {!Long} Product
* @expose
*/
LongPrototype.multiply = function multiply(multiplier) {
if (this.isZero())
@ -26248,6 +26299,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} multiplier Multiplier
* @returns {!Long} Product
* @expose
*/
LongPrototype.mul = LongPrototype.multiply;
@ -26256,6 +26308,7 @@ Curve25519Worker.prototype = {
* unsigned if this Long is unsigned.
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.divide = function divide(divisor) {
if (!isLong(divisor))
@ -26266,8 +26319,6 @@ Curve25519Worker.prototype = {
return this.unsigned ? UZERO : ZERO;
var approx, rem, res;
if (!this.unsigned) {
// This section is only relevant for signed longs and is derived from the
// closure library as a whole.
if (this.eq(MIN_VALUE)) {
if (divisor.eq(ONE) || divisor.eq(NEG_ONE))
return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
@ -26293,18 +26344,19 @@ Curve25519Worker.prototype = {
return this.neg().div(divisor).neg();
} else if (divisor.isNegative())
return this.div(divisor.neg()).neg();
res = ZERO;
} else {
// The algorithm below has not been made for unsigned longs. It's therefore
// required to take special care of the MSB prior to running it.
if (!divisor.unsigned)
divisor = divisor.toUnsigned();
} else if (!divisor.unsigned)
divisor = divisor.toUnsigned();
// The algorithm below has not been made for unsigned longs. It's therefore
// required to take special care of the MSB prior to running it.
if (this.unsigned) {
if (divisor.gt(this))
return UZERO;
if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true
return UONE;
res = UZERO;
}
} else
res = ZERO;
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
@ -26348,6 +26400,7 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.div = LongPrototype.divide;
@ -26355,6 +26408,7 @@ Curve25519Worker.prototype = {
* Returns this Long modulo the specified.
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Remainder
* @expose
*/
LongPrototype.modulo = function modulo(divisor) {
if (!isLong(divisor))
@ -26367,12 +26421,14 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Remainder
* @expose
*/
LongPrototype.mod = LongPrototype.modulo;
/**
* Returns the bitwise NOT of this Long.
* @returns {!Long}
* @expose
*/
LongPrototype.not = function not() {
return fromBits(~this.low, ~this.high, this.unsigned);
@ -26382,6 +26438,7 @@ Curve25519Worker.prototype = {
* Returns the bitwise AND of this Long and the specified.
* @param {!Long|number|string} other Other Long
* @returns {!Long}
* @expose
*/
LongPrototype.and = function and(other) {
if (!isLong(other))
@ -26393,6 +26450,7 @@ Curve25519Worker.prototype = {
* Returns the bitwise OR of this Long and the specified.
* @param {!Long|number|string} other Other Long
* @returns {!Long}
* @expose
*/
LongPrototype.or = function or(other) {
if (!isLong(other))
@ -26404,6 +26462,7 @@ Curve25519Worker.prototype = {
* Returns the bitwise XOR of this Long and the given one.
* @param {!Long|number|string} other Other Long
* @returns {!Long}
* @expose
*/
LongPrototype.xor = function xor(other) {
if (!isLong(other))
@ -26415,6 +26474,7 @@ Curve25519Worker.prototype = {
* Returns this Long with bits shifted to the left by the given amount.
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shiftLeft = function shiftLeft(numBits) {
if (isLong(numBits))
@ -26432,6 +26492,7 @@ Curve25519Worker.prototype = {
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shl = LongPrototype.shiftLeft;
@ -26439,6 +26500,7 @@ Curve25519Worker.prototype = {
* Returns this Long with bits arithmetically shifted to the right by the given amount.
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shiftRight = function shiftRight(numBits) {
if (isLong(numBits))
@ -26456,6 +26518,7 @@ Curve25519Worker.prototype = {
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shr = LongPrototype.shiftRight;
@ -26463,6 +26526,7 @@ Curve25519Worker.prototype = {
* Returns this Long with bits logically shifted to the right by the given amount.
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) {
if (isLong(numBits))
@ -26487,12 +26551,14 @@ Curve25519Worker.prototype = {
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shru = LongPrototype.shiftRightUnsigned;
/**
* Converts this Long to signed.
* @returns {!Long} Signed long
* @expose
*/
LongPrototype.toSigned = function toSigned() {
if (!this.unsigned)
@ -26503,6 +26569,7 @@ Curve25519Worker.prototype = {
/**
* Converts this Long to unsigned.
* @returns {!Long} Unsigned long
* @expose
*/
LongPrototype.toUnsigned = function toUnsigned() {
if (this.unsigned)
@ -26510,53 +26577,6 @@ Curve25519Worker.prototype = {
return fromBits(this.low, this.high, true);
};
/**
* Converts this Long to its byte representation.
* @param {boolean=} le Whether little or big endian, defaults to big endian
* @returns {!Array.<number>} Byte representation
*/
LongPrototype.toBytes = function(le) {
return le ? this.toBytesLE() : this.toBytesBE();
}
/**
* Converts this Long to its little endian byte representation.
* @returns {!Array.<number>} Little endian byte representation
*/
LongPrototype.toBytesLE = function() {
var hi = this.high,
lo = this.low;
return [
lo & 0xff,
(lo >>> 8) & 0xff,
(lo >>> 16) & 0xff,
(lo >>> 24) & 0xff,
hi & 0xff,
(hi >>> 8) & 0xff,
(hi >>> 16) & 0xff,
(hi >>> 24) & 0xff
];
}
/**
* Converts this Long to its big endian byte representation.
* @returns {!Array.<number>} Big endian byte representation
*/
LongPrototype.toBytesBE = function() {
var hi = this.high,
lo = this.low;
return [
(hi >>> 24) & 0xff,
(hi >>> 16) & 0xff,
(hi >>> 8) & 0xff,
hi & 0xff,
(lo >>> 24) & 0xff,
(lo >>> 16) & 0xff,
(lo >>> 8) & 0xff,
lo & 0xff
];
}
return Long;
});
@ -35291,6 +35311,8 @@ var Internal = Internal || {};
result = result | (a[i] ^ b[i]);
}
if (result !== 0) {
console.log('Our MAC ', dcodeIO.ByteBuffer.wrap(calculated_mac).toHex());
console.log('Their MAC', dcodeIO.ByteBuffer.wrap(mac).toHex());
throw new Error("Bad MAC");
}
});
@ -35493,7 +35515,6 @@ Internal.ChainType = {
Internal.SessionRecord = function() {
'use strict';
var ARCHIVED_STATES_MAX_LENGTH = 40;
var OLD_RATCHETS_MAX_LENGTH = 10;
var SESSION_RECORD_VERSION = 'v1';
var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__;
@ -35520,12 +35541,7 @@ Internal.SessionRecord = function() {
} else if (thing === Object(thing)) {
var obj = {};
for (var key in thing) {
try {
obj[key] = ensureStringed(thing[key]);
} catch (ex) {
console.log('Error serializing key', key);
throw ex;
}
obj[key] = ensureStringed(thing[key]);
}
return obj;
} else if (thing === null) {
@ -35544,21 +35560,12 @@ Internal.SessionRecord = function() {
version: 'v1',
migrate: function migrateV1(data) {
var sessions = data.sessions;
var key;
if (data.registrationId) {
for (key in sessions) {
for (var key in sessions) {
if (!sessions[key].registrationId) {
sessions[key].registrationId = data.registrationId;
}
}
} else {
for (key in sessions) {
if (sessions[key].indexInfo.closed === -1) {
console.log('V1 session storage migration error: registrationId',
data.registrationId, 'for open session version',
data.version);
}
}
}
}
}
@ -35579,7 +35586,7 @@ Internal.SessionRecord = function() {
}
var SessionRecord = function() {
this.sessions = {};
this._sessions = {};
this.version = SESSION_RECORD_VERSION;
};
@ -35588,8 +35595,8 @@ Internal.SessionRecord = function() {
if (data.version !== SESSION_RECORD_VERSION) { migrate(data); }
var record = new SessionRecord();
record.sessions = data.sessions;
if (record.sessions === undefined || record.sessions === null || typeof record.sessions !== "object" || Array.isArray(record.sessions)) {
record._sessions = data.sessions;
if (record._sessions === undefined || record._sessions === null || typeof record._sessions !== "object" || Array.isArray(record._sessions)) {
throw new Error("Error deserializing SessionRecord");
}
return record;
@ -35598,17 +35605,16 @@ Internal.SessionRecord = function() {
SessionRecord.prototype = {
serialize: function() {
return jsonThing({
sessions : this.sessions,
sessions : this._sessions,
version : this.version
});
},
haveOpenSession: function() {
var openSession = this.getOpenSession();
return (!!openSession && typeof openSession.registrationId === 'number');
return this.getOpenSession() !== undefined;
},
getSessionByBaseKey: function(baseKey) {
var session = this.sessions[util.toString(baseKey)];
var session = this._sessions[util.toString(baseKey)];
if (session && session.indexInfo.baseKeyType === Internal.BaseKeyType.OURS) {
console.log("Tried to lookup a session using our basekey");
return undefined;
@ -35617,7 +35623,7 @@ Internal.SessionRecord = function() {
},
getSessionByRemoteEphemeralKey: function(remoteEphemeralKey) {
this.detectDuplicateOpenSessions();
var sessions = this.sessions;
var sessions = this._sessions;
var searchKey = util.toString(remoteEphemeralKey);
@ -35637,7 +35643,7 @@ Internal.SessionRecord = function() {
return undefined;
},
getOpenSession: function() {
var sessions = this.sessions;
var sessions = this._sessions;
if (sessions === undefined) {
return undefined;
}
@ -35653,7 +35659,7 @@ Internal.SessionRecord = function() {
},
detectDuplicateOpenSessions: function() {
var openSession;
var sessions = this.sessions;
var sessions = this._sessions;
for (var key in sessions) {
if (sessions[key].indexInfo.closed == -1) {
if (openSession !== undefined) {
@ -35664,7 +35670,7 @@ Internal.SessionRecord = function() {
}
},
updateSessionState: function(session) {
var sessions = this.sessions;
var sessions = this._sessions;
this.removeOldChains(session);
@ -35678,11 +35684,11 @@ Internal.SessionRecord = function() {
// followed by the open session
var list = [];
var openSession;
for (var k in this.sessions) {
if (this.sessions[k].indexInfo.closed === -1) {
openSession = this.sessions[k];
for (var k in this._sessions) {
if (this._sessions[k].indexInfo.closed === -1) {
openSession = this._sessions[k];
} else {
list.push(this.sessions[k]);
list.push(this._sessions[k]);
}
}
list = list.sort(function(s1, s2) {
@ -35696,20 +35702,38 @@ Internal.SessionRecord = function() {
archiveCurrentState: function() {
var open_session = this.getOpenSession();
if (open_session !== undefined) {
console.log('closing session');
open_session.indexInfo.closed = Date.now();
this.closeSession(open_session);
this.updateSessionState(open_session);
}
},
promoteState: function(session) {
console.log('promoting session');
session.indexInfo.closed = -1;
closeSession: function(session) {
if (session.indexInfo.closed > -1) {
return;
}
console.log('closing session', session.indexInfo.baseKey);
// After this has run, we can still receive messages on ratchet chains which
// were already open (unless we know we dont need them),
// but we cannot send messages or step the ratchet
// Delete current sending ratchet
delete session[util.toString(session.currentRatchet.ephemeralKeyPair.pubKey)];
// Move all receive ratchets to the oldRatchetList to mark them for deletion
for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
session.oldRatchetList[session.oldRatchetList.length] = {
added: Date.now(), ephemeralKey: i
};
}
}
session.indexInfo.closed = Date.now();
this.removeOldChains(session);
},
removeOldChains: function(session) {
// Sending ratchets are always removed when we step because we never need them again
// Receiving ratchets are added to the oldRatchetList, which we parse
// here and remove all but the last ten.
while (session.oldRatchetList.length > OLD_RATCHETS_MAX_LENGTH) {
// here and remove all but the last five.
while (session.oldRatchetList.length > 5) {
var index = 0;
var oldest = session.oldRatchetList[0];
for (var i = 0; i < session.oldRatchetList.length; i++) {
@ -35725,7 +35749,7 @@ Internal.SessionRecord = function() {
},
removeOldSessions: function() {
// Retain only the last 20 sessions
var sessions = this.sessions;
var sessions = this._sessions;
var oldestBaseKey, oldestSession;
while (Object.keys(sessions).length > ARCHIVED_STATES_MAX_LENGTH) {
for (var key in sessions) {
@ -35740,10 +35764,6 @@ Internal.SessionRecord = function() {
delete sessions[util.toString(oldestBaseKey)];
}
},
deleteAllSessions: function() {
// Used primarily in session reset scenarios, where we really delete sessions
this.sessions = {};
}
};
return SessionRecord;
@ -35795,7 +35815,7 @@ SessionBuilder.prototype = {
processPreKey: function(device) {
return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING
this.remoteAddress.getName(), device.identityKey
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
@ -35809,20 +35829,15 @@ SessionBuilder.prototype = {
}).then(function() {
return Internal.crypto.createKeyPair();
}).then(function(baseKey) {
var devicePreKey;
if (device.preKey) {
devicePreKey = device.preKey.publicKey;
}
var devicePreKey = (device.preKey.publicKey);
return this.initSession(true, baseKey, undefined, device.identityKey,
devicePreKey, device.signedPreKey.publicKey, device.registrationId
).then(function(session) {
session.pendingPreKey = {
preKeyId : device.preKey.keyId,
signedKeyId : device.signedPreKey.keyId,
baseKey : baseKey.pubKey
};
if (device.preKey) {
session.pendingPreKey.preKeyId = device.preKey.keyId;
}
return session;
});
}.bind(this)).then(function(session) {
@ -35836,10 +35851,10 @@ SessionBuilder.prototype = {
}
record.archiveCurrentState();
record.updateSessionState(session);
record.updateSessionState(session, device.registrationId);
return Promise.all([
this.storage.storeSession(address, record.serialize()),
this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey)
this.storage.saveIdentity(this.remoteAddress.getName(), session.indexInfo.remoteIdentityKey)
]);
}.bind(this));
}.bind(this));
@ -35848,7 +35863,7 @@ SessionBuilder.prototype = {
processV3: function(record, message) {
var preKeyPair, signedPreKeyPair, session;
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING
this.remoteAddress.getName(), message.identityKey.toArrayBuffer()
).then(function(trusted) {
if (!trusted) {
var e = new Error('Unknown identity key');
@ -35895,8 +35910,8 @@ SessionBuilder.prototype = {
// Note that the session is not actually saved until the very
// end of decryptWhisperMessage ... to ensure that the sender
// actually holds the private keys for all reported pubkeys
record.updateSessionState(new_session);
return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() {
record.updateSessionState(new_session, message.registrationId);
return this.storage.saveIdentity(this.remoteAddress.getName(), message.identityKey.toArrayBuffer()).then(function() {
return message.preKeyId;
});
}.bind(this));
@ -36092,20 +36107,10 @@ SessionCipher.prototype = {
result.set(new Uint8Array(encodedMsg), 1);
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), util.toArrayBuffer(session.indexInfo.remoteIdentityKey), this.storage.Direction.SENDING
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
}
}).then(function() {
return this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey);
}.bind(this)).then(function() {
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result;
});
}.bind(this));
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result;
});
}.bind(this));
}.bind(this));
}.bind(this)).then(function(message) {
@ -36115,9 +36120,7 @@ SessionCipher.prototype = {
preKeyMsg.registrationId = myRegistrationId;
preKeyMsg.baseKey = util.toArrayBuffer(session.pendingPreKey.baseKey);
if (session.pendingPreKey.preKeyId) {
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
}
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
preKeyMsg.message = message;
@ -36150,10 +36153,6 @@ SessionCipher.prototype = {
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
return { plaintext: plaintext, session: session };
}).catch(function(e) {
if (e.name === 'MessageCounterError') {
return Promise.reject(e);
}
errors.push(e);
return this.decryptWithSessionList(buffer, sessionList, errors);
}.bind(this));
@ -36169,25 +36168,10 @@ SessionCipher.prototype = {
var errors = [];
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
return this.getRecord(address).then(function(record) {
if (result.session.indexInfo.baseKey !== record.getOpenSession().indexInfo.baseKey) {
record.archiveCurrentState();
record.promoteState(result.session);
}
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), util.toArrayBuffer(result.session.indexInfo.remoteIdentityKey), this.storage.Direction.RECEIVING
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
}
}).then(function() {
return this.storage.saveIdentity(this.remoteAddress.toString(), result.session.indexInfo.remoteIdentityKey);
}.bind(this)).then(function() {
record.updateSessionState(result.session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result.plaintext;
});
}.bind(this));
record.updateSessionState(result.session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result.plaintext;
});
}.bind(this));
}.bind(this));
}.bind(this));
@ -36212,7 +36196,6 @@ SessionCipher.prototype = {
);
}
var builder = new SessionBuilder(this.storage, this.remoteAddress);
// isTrustedIdentity is called within processV3, no need to call it here
return builder.processV3(record, preKeyProto).then(function(preKeyId) {
var session = record.getSessionByBaseKey(preKeyProto.baseKey);
return this.doDecryptWhisperMessage(
@ -36220,7 +36203,7 @@ SessionCipher.prototype = {
).then(function(plaintext) {
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
if (preKeyId !== undefined && preKeyId !== null) {
if (preKeyId !== undefined) {
return this.storage.removePreKey(preKeyId);
}
}.bind(this)).then(function() {
@ -36232,7 +36215,7 @@ SessionCipher.prototype = {
}.bind(this));
},
doDecryptWhisperMessage: function(messageBytes, session) {
if (!(messageBytes instanceof ArrayBuffer)) {
if (!messageBytes instanceof ArrayBuffer) {
throw new Error("Expected messageBytes to be an ArrayBuffer");
}
var version = (new Uint8Array(messageBytes))[0];
@ -36287,12 +36270,13 @@ SessionCipher.prototype = {
});
},
fillMessageKeys: function(chain, counter) {
if (chain.chainKey.counter >= counter) {
return Promise.resolve(); // Already calculated
if (Object.keys(chain.messageKeys).length >= 1000) {
console.log("Too many message keys for chain");
return Promise.resolve(); // Stalker, much?
}
if (counter - chain.chainKey.counter > 2000) {
throw new Error('Over 2000 messages into the future!');
if (chain.chainKey.counter >= counter) {
return Promise.resolve(); // Already calculated
}
if (chain.chainKey.key === undefined) {
@ -36406,20 +36390,6 @@ SessionCipher.prototype = {
return this.storage.storeSession(address, record.serialize());
}.bind(this));
}.bind(this));
},
deleteAllSessionsForDevice: function() {
// Used in session reset scenarios, where we really need to delete
var address = this.remoteAddress.toString();
return Internal.SessionLock.queueJobForNumber(address, function() {
return this.getRecord(address).then(function(record) {
if (record === undefined) {
return;
}
record.deleteAllSessions();
return this.storage.storeSession(address, record.serialize());
}.bind(this));
}.bind(this));
}
};
@ -36439,7 +36409,6 @@ libsignal.SessionCipher = function(storage, remoteAddress) {
this.getRemoteRegistrationId = cipher.getRemoteRegistrationId.bind(cipher);
this.hasOpenSession = cipher.hasOpenSession.bind(cipher);
this.closeOpenSessionForDevice = cipher.closeOpenSessionForDevice.bind(cipher);
this.deleteAllSessionsForDevice = cipher.deleteAllSessionsForDevice.bind(cipher);
};
/*

View File

@ -1,7 +1,7 @@
{
"name": "libsignal-protocol",
"repository": "https://github.com/WhisperSystems/libsignal-protocol-javascript.git",
"version": "1.3.0",
"version": "1.1.2",
"license": "GPL-3.0",
"dependencies": {
"long": "^3.1.0",
@ -15,7 +15,7 @@
"grunt-cli": "^0.1.13",
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-connect": "^0.9.0",
"grunt-contrib-jshint": "^1.1.0",
"grunt-contrib-jshint": "^0.10.0",
"grunt-contrib-sass": "^0.8.1",
"grunt-contrib-watch": "^0.6.1",
"grunt-jscs": "^1.1.0",

View File

@ -7,7 +7,7 @@ SessionBuilder.prototype = {
processPreKey: function(device) {
return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING
this.remoteAddress.getName(), device.identityKey
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
@ -21,20 +21,15 @@ SessionBuilder.prototype = {
}).then(function() {
return Internal.crypto.createKeyPair();
}).then(function(baseKey) {
var devicePreKey;
if (device.preKey) {
devicePreKey = device.preKey.publicKey;
}
var devicePreKey = (device.preKey.publicKey);
return this.initSession(true, baseKey, undefined, device.identityKey,
devicePreKey, device.signedPreKey.publicKey, device.registrationId
).then(function(session) {
session.pendingPreKey = {
preKeyId : device.preKey.keyId,
signedKeyId : device.signedPreKey.keyId,
baseKey : baseKey.pubKey
};
if (device.preKey) {
session.pendingPreKey.preKeyId = device.preKey.keyId;
}
return session;
});
}.bind(this)).then(function(session) {
@ -48,10 +43,10 @@ SessionBuilder.prototype = {
}
record.archiveCurrentState();
record.updateSessionState(session);
record.updateSessionState(session, device.registrationId);
return Promise.all([
this.storage.storeSession(address, record.serialize()),
this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey)
this.storage.saveIdentity(this.remoteAddress.getName(), session.indexInfo.remoteIdentityKey)
]);
}.bind(this));
}.bind(this));
@ -60,7 +55,7 @@ SessionBuilder.prototype = {
processV3: function(record, message) {
var preKeyPair, signedPreKeyPair, session;
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING
this.remoteAddress.getName(), message.identityKey.toArrayBuffer()
).then(function(trusted) {
if (!trusted) {
var e = new Error('Unknown identity key');
@ -107,8 +102,8 @@ SessionBuilder.prototype = {
// Note that the session is not actually saved until the very
// end of decryptWhisperMessage ... to ensure that the sender
// actually holds the private keys for all reported pubkeys
record.updateSessionState(new_session);
return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() {
record.updateSessionState(new_session, message.registrationId);
return this.storage.saveIdentity(this.remoteAddress.getName(), message.identityKey.toArrayBuffer()).then(function() {
return message.preKeyId;
});
}.bind(this));

View File

@ -76,20 +76,10 @@ SessionCipher.prototype = {
result.set(new Uint8Array(encodedMsg), 1);
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), util.toArrayBuffer(session.indexInfo.remoteIdentityKey), this.storage.Direction.SENDING
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
}
}).then(function() {
return this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey);
}.bind(this)).then(function() {
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result;
});
}.bind(this));
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result;
});
}.bind(this));
}.bind(this));
}.bind(this)).then(function(message) {
@ -99,9 +89,7 @@ SessionCipher.prototype = {
preKeyMsg.registrationId = myRegistrationId;
preKeyMsg.baseKey = util.toArrayBuffer(session.pendingPreKey.baseKey);
if (session.pendingPreKey.preKeyId) {
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
}
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
preKeyMsg.message = message;
@ -134,10 +122,6 @@ SessionCipher.prototype = {
return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
return { plaintext: plaintext, session: session };
}).catch(function(e) {
if (e.name === 'MessageCounterError') {
return Promise.reject(e);
}
errors.push(e);
return this.decryptWithSessionList(buffer, sessionList, errors);
}.bind(this));
@ -153,25 +137,10 @@ SessionCipher.prototype = {
var errors = [];
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
return this.getRecord(address).then(function(record) {
if (result.session.indexInfo.baseKey !== record.getOpenSession().indexInfo.baseKey) {
record.archiveCurrentState();
record.promoteState(result.session);
}
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), util.toArrayBuffer(result.session.indexInfo.remoteIdentityKey), this.storage.Direction.RECEIVING
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
}
}).then(function() {
return this.storage.saveIdentity(this.remoteAddress.toString(), result.session.indexInfo.remoteIdentityKey);
}.bind(this)).then(function() {
record.updateSessionState(result.session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result.plaintext;
});
}.bind(this));
record.updateSessionState(result.session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result.plaintext;
});
}.bind(this));
}.bind(this));
}.bind(this));
@ -196,7 +165,6 @@ SessionCipher.prototype = {
);
}
var builder = new SessionBuilder(this.storage, this.remoteAddress);
// isTrustedIdentity is called within processV3, no need to call it here
return builder.processV3(record, preKeyProto).then(function(preKeyId) {
var session = record.getSessionByBaseKey(preKeyProto.baseKey);
return this.doDecryptWhisperMessage(
@ -204,7 +172,7 @@ SessionCipher.prototype = {
).then(function(plaintext) {
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
if (preKeyId !== undefined && preKeyId !== null) {
if (preKeyId !== undefined) {
return this.storage.removePreKey(preKeyId);
}
}.bind(this)).then(function() {
@ -216,7 +184,7 @@ SessionCipher.prototype = {
}.bind(this));
},
doDecryptWhisperMessage: function(messageBytes, session) {
if (!(messageBytes instanceof ArrayBuffer)) {
if (!messageBytes instanceof ArrayBuffer) {
throw new Error("Expected messageBytes to be an ArrayBuffer");
}
var version = (new Uint8Array(messageBytes))[0];
@ -271,12 +239,13 @@ SessionCipher.prototype = {
});
},
fillMessageKeys: function(chain, counter) {
if (chain.chainKey.counter >= counter) {
return Promise.resolve(); // Already calculated
if (Object.keys(chain.messageKeys).length >= 1000) {
console.log("Too many message keys for chain");
return Promise.resolve(); // Stalker, much?
}
if (counter - chain.chainKey.counter > 2000) {
throw new Error('Over 2000 messages into the future!');
if (chain.chainKey.counter >= counter) {
return Promise.resolve(); // Already calculated
}
if (chain.chainKey.key === undefined) {
@ -390,20 +359,6 @@ SessionCipher.prototype = {
return this.storage.storeSession(address, record.serialize());
}.bind(this));
}.bind(this));
},
deleteAllSessionsForDevice: function() {
// Used in session reset scenarios, where we really need to delete
var address = this.remoteAddress.toString();
return Internal.SessionLock.queueJobForNumber(address, function() {
return this.getRecord(address).then(function(record) {
if (record === undefined) {
return;
}
record.deleteAllSessions();
return this.storage.storeSession(address, record.serialize());
}.bind(this));
}.bind(this));
}
};
@ -423,5 +378,4 @@ libsignal.SessionCipher = function(storage, remoteAddress) {
this.getRemoteRegistrationId = cipher.getRemoteRegistrationId.bind(cipher);
this.hasOpenSession = cipher.hasOpenSession.bind(cipher);
this.closeOpenSessionForDevice = cipher.closeOpenSessionForDevice.bind(cipher);
this.deleteAllSessionsForDevice = cipher.deleteAllSessionsForDevice.bind(cipher);
};

View File

@ -16,7 +16,6 @@ Internal.ChainType = {
Internal.SessionRecord = function() {
'use strict';
var ARCHIVED_STATES_MAX_LENGTH = 40;
var OLD_RATCHETS_MAX_LENGTH = 10;
var SESSION_RECORD_VERSION = 'v1';
var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__;
@ -43,12 +42,7 @@ Internal.SessionRecord = function() {
} else if (thing === Object(thing)) {
var obj = {};
for (var key in thing) {
try {
obj[key] = ensureStringed(thing[key]);
} catch (ex) {
console.log('Error serializing key', key);
throw ex;
}
obj[key] = ensureStringed(thing[key]);
}
return obj;
} else if (thing === null) {
@ -67,21 +61,12 @@ Internal.SessionRecord = function() {
version: 'v1',
migrate: function migrateV1(data) {
var sessions = data.sessions;
var key;
if (data.registrationId) {
for (key in sessions) {
for (var key in sessions) {
if (!sessions[key].registrationId) {
sessions[key].registrationId = data.registrationId;
}
}
} else {
for (key in sessions) {
if (sessions[key].indexInfo.closed === -1) {
console.log('V1 session storage migration error: registrationId',
data.registrationId, 'for open session version',
data.version);
}
}
}
}
}
@ -102,7 +87,7 @@ Internal.SessionRecord = function() {
}
var SessionRecord = function() {
this.sessions = {};
this._sessions = {};
this.version = SESSION_RECORD_VERSION;
};
@ -111,8 +96,8 @@ Internal.SessionRecord = function() {
if (data.version !== SESSION_RECORD_VERSION) { migrate(data); }
var record = new SessionRecord();
record.sessions = data.sessions;
if (record.sessions === undefined || record.sessions === null || typeof record.sessions !== "object" || Array.isArray(record.sessions)) {
record._sessions = data.sessions;
if (record._sessions === undefined || record._sessions === null || typeof record._sessions !== "object" || Array.isArray(record._sessions)) {
throw new Error("Error deserializing SessionRecord");
}
return record;
@ -121,17 +106,16 @@ Internal.SessionRecord = function() {
SessionRecord.prototype = {
serialize: function() {
return jsonThing({
sessions : this.sessions,
sessions : this._sessions,
version : this.version
});
},
haveOpenSession: function() {
var openSession = this.getOpenSession();
return (!!openSession && typeof openSession.registrationId === 'number');
return this.getOpenSession() !== undefined;
},
getSessionByBaseKey: function(baseKey) {
var session = this.sessions[util.toString(baseKey)];
var session = this._sessions[util.toString(baseKey)];
if (session && session.indexInfo.baseKeyType === Internal.BaseKeyType.OURS) {
console.log("Tried to lookup a session using our basekey");
return undefined;
@ -140,7 +124,7 @@ Internal.SessionRecord = function() {
},
getSessionByRemoteEphemeralKey: function(remoteEphemeralKey) {
this.detectDuplicateOpenSessions();
var sessions = this.sessions;
var sessions = this._sessions;
var searchKey = util.toString(remoteEphemeralKey);
@ -160,7 +144,7 @@ Internal.SessionRecord = function() {
return undefined;
},
getOpenSession: function() {
var sessions = this.sessions;
var sessions = this._sessions;
if (sessions === undefined) {
return undefined;
}
@ -176,7 +160,7 @@ Internal.SessionRecord = function() {
},
detectDuplicateOpenSessions: function() {
var openSession;
var sessions = this.sessions;
var sessions = this._sessions;
for (var key in sessions) {
if (sessions[key].indexInfo.closed == -1) {
if (openSession !== undefined) {
@ -187,7 +171,7 @@ Internal.SessionRecord = function() {
}
},
updateSessionState: function(session) {
var sessions = this.sessions;
var sessions = this._sessions;
this.removeOldChains(session);
@ -201,11 +185,11 @@ Internal.SessionRecord = function() {
// followed by the open session
var list = [];
var openSession;
for (var k in this.sessions) {
if (this.sessions[k].indexInfo.closed === -1) {
openSession = this.sessions[k];
for (var k in this._sessions) {
if (this._sessions[k].indexInfo.closed === -1) {
openSession = this._sessions[k];
} else {
list.push(this.sessions[k]);
list.push(this._sessions[k]);
}
}
list = list.sort(function(s1, s2) {
@ -219,20 +203,38 @@ Internal.SessionRecord = function() {
archiveCurrentState: function() {
var open_session = this.getOpenSession();
if (open_session !== undefined) {
console.log('closing session');
open_session.indexInfo.closed = Date.now();
this.closeSession(open_session);
this.updateSessionState(open_session);
}
},
promoteState: function(session) {
console.log('promoting session');
session.indexInfo.closed = -1;
closeSession: function(session) {
if (session.indexInfo.closed > -1) {
return;
}
console.log('closing session', session.indexInfo.baseKey);
// After this has run, we can still receive messages on ratchet chains which
// were already open (unless we know we dont need them),
// but we cannot send messages or step the ratchet
// Delete current sending ratchet
delete session[util.toString(session.currentRatchet.ephemeralKeyPair.pubKey)];
// Move all receive ratchets to the oldRatchetList to mark them for deletion
for (var i in session) {
if (session[i].chainKey !== undefined && session[i].chainKey.key !== undefined) {
session.oldRatchetList[session.oldRatchetList.length] = {
added: Date.now(), ephemeralKey: i
};
}
}
session.indexInfo.closed = Date.now();
this.removeOldChains(session);
},
removeOldChains: function(session) {
// Sending ratchets are always removed when we step because we never need them again
// Receiving ratchets are added to the oldRatchetList, which we parse
// here and remove all but the last ten.
while (session.oldRatchetList.length > OLD_RATCHETS_MAX_LENGTH) {
// here and remove all but the last five.
while (session.oldRatchetList.length > 5) {
var index = 0;
var oldest = session.oldRatchetList[0];
for (var i = 0; i < session.oldRatchetList.length; i++) {
@ -248,7 +250,7 @@ Internal.SessionRecord = function() {
},
removeOldSessions: function() {
// Retain only the last 20 sessions
var sessions = this.sessions;
var sessions = this._sessions;
var oldestBaseKey, oldestSession;
while (Object.keys(sessions).length > ARCHIVED_STATES_MAX_LENGTH) {
for (var key in sessions) {
@ -263,10 +265,6 @@ Internal.SessionRecord = function() {
delete sessions[util.toString(oldestBaseKey)];
}
},
deleteAllSessions: function() {
// Used primarily in session reset scenarios, where we really delete sessions
this.sessions = {};
}
};
return SessionRecord;

View File

@ -101,6 +101,8 @@ var Internal = Internal || {};
result = result | (a[i] ^ b[i]);
}
if (result !== 0) {
console.log('Our MAC ', dcodeIO.ByteBuffer.wrap(calculated_mac).toHex());
console.log('Their MAC', dcodeIO.ByteBuffer.wrap(mac).toHex());
throw new Error("Bad MAC");
}
});

View File

@ -1,7 +1,6 @@
function testIdentityKeyStore(store, registrationId, identityKey) {
describe('IdentityKeyStore', function() {
var number = '+5558675309';
var address = new libsignal.SignalProtocolAddress('+5558675309', 1);
var testKey;
before(function(done) {
Internal.crypto.createKeyPair().then(function(keyPair) {
@ -26,7 +25,7 @@ function testIdentityKeyStore(store, registrationId, identityKey) {
});
describe('saveIdentity', function() {
it('stores identity keys', function(done) {
store.saveIdentity(address.toString(), testKey.pubKey).then(function() {
store.saveIdentity(number, testKey.pubKey).then(function() {
return store.loadIdentityKey(number).then(function(key) {
assertEqualArrayBuffers(key, testKey.pubKey);
});
@ -35,7 +34,7 @@ function testIdentityKeyStore(store, registrationId, identityKey) {
});
describe('isTrustedIdentity', function() {
it('returns true if a key is trusted', function(done) {
store.saveIdentity(address.toString(), testKey.pubKey).then(function() {
store.saveIdentity(number, testKey.pubKey).then(function() {
store.isTrustedIdentity(number, testKey.pubKey).then(function(trusted) {
if (trusted) {
done();
@ -47,7 +46,7 @@ function testIdentityKeyStore(store, registrationId, identityKey) {
});
it('returns false if a key is untrusted', function(done) {
var newIdentity = libsignal.crypto.getRandomBytes(33);
store.saveIdentity(address.toString(), testKey.pubKey).then(function() {
store.saveIdentity(number, testKey.pubKey).then(function() {
store.isTrustedIdentity(number, newIdentity).then(function(trusted) {
if (trusted) {
done(new Error('Wrong value for untrusted key'));

View File

@ -1,112 +1,96 @@
function SignalProtocolStore() {
this.store = {};
function SignalProtocolStore() {
this.store = {};
}
SignalProtocolStore.prototype = {
Direction: {
SENDING: 1,
RECEIVING: 2,
},
getIdentityKeyPair: function() {
return Promise.resolve(this.get('identityKey'));
},
getLocalRegistrationId: function() {
return Promise.resolve(this.get('registrationId'));
},
put: function(key, value) {
if (key === undefined || value === undefined || key === null || value === null)
throw new Error("Tried to store undefined/null");
this.store[key] = value;
},
get: function(key, defaultValue) {
if (key === null || key === undefined)
throw new Error("Tried to get value for undefined/null key");
if (key in this.store) {
return this.store[key];
} else {
return defaultValue;
}
},
remove: function(key) {
if (key === null || key === undefined)
throw new Error("Tried to remove value for undefined/null key");
delete this.store[key];
},
getIdentityKeyPair: function() {
return Promise.resolve(this.get('identityKey'));
},
getLocalRegistrationId: function() {
return Promise.resolve(this.get('registrationId'));
},
put: function(key, value) {
if (key === undefined || value === undefined || key === null || value === null)
throw new Error("Tried to store undefined/null");
this.store[key] = value;
},
get: function(key, defaultValue) {
if (key === null || key === undefined)
throw new Error("Tried to get value for undefined/null key");
if (key in this.store) {
return this.store[key];
} else {
return defaultValue;
isTrustedIdentity: function(identifier, identityKey) {
if (identifier === null || identifier === undefined) {
throw new Error("tried to check identity key for undefined/null key");
}
},
remove: function(key) {
if (key === null || key === undefined)
throw new Error("Tried to remove value for undefined/null key");
delete this.store[key];
},
isTrustedIdentity: function(identifier, identityKey, direction) {
if (identifier === null || identifier === undefined) {
throw new Error("tried to check identity key for undefined/null key");
if (!(identityKey instanceof ArrayBuffer)) {
throw new Error("Expected identityKey to be an ArrayBuffer");
}
if (!(identityKey instanceof ArrayBuffer)) {
throw new Error("Expected identityKey to be an ArrayBuffer");
}
var trusted = this.get('identityKey' + identifier);
var trusted = this.get('identityKey' + identifier);
if (trusted === undefined) {
return Promise.resolve(true);
}
return Promise.resolve(util.toString(identityKey) === util.toString(trusted));
},
loadIdentityKey: function(identifier) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to get identity key for undefined/null key");
return Promise.resolve(this.get('identityKey' + identifier));
},
saveIdentity: function(identifier, identityKey) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to put identity key for undefined/null key");
},
loadIdentityKey: function(identifier) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to get identity key for undefined/null key");
return Promise.resolve(this.get('identityKey' + identifier));
},
saveIdentity: function(identifier, identityKey) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to put identity key for undefined/null key");
return Promise.resolve(this.put('identityKey' + identifier, identityKey));
},
var address = new libsignal.SignalProtocolAddress.fromString(identifier);
var existing = this.get('identityKey' + address.getName());
this.put('identityKey' + address.getName(), identityKey)
if (existing && util.toString(identityKey) !== util.toString(existing)) {
return Promise.resolve(true);
} else {
return Promise.resolve(false);
}
},
/* Returns a prekeypair object or undefined */
loadPreKey: function(keyId) {
/* Returns a prekeypair object or undefined */
loadPreKey: function(keyId) {
var res = this.get('25519KeypreKey' + keyId);
if (res !== undefined) {
res = { pubKey: res.pubKey, privKey: res.privKey };
}
return Promise.resolve(res);
},
storePreKey: function(keyId, keyPair) {
return Promise.resolve(this.put('25519KeypreKey' + keyId, keyPair));
},
removePreKey: function(keyId) {
return Promise.resolve(this.remove('25519KeypreKey' + keyId));
},
},
storePreKey: function(keyId, keyPair) {
return Promise.resolve(this.put('25519KeypreKey' + keyId, keyPair));
},
removePreKey: function(keyId) {
return Promise.resolve(this.remove('25519KeypreKey' + keyId));
},
/* Returns a signed keypair object or undefined */
loadSignedPreKey: function(keyId) {
/* Returns a signed keypair object or undefined */
loadSignedPreKey: function(keyId) {
var res = this.get('25519KeysignedKey' + keyId);
if (res !== undefined) {
res = { pubKey: res.pubKey, privKey: res.privKey };
}
return Promise.resolve(res);
},
storeSignedPreKey: function(keyId, keyPair) {
return Promise.resolve(this.put('25519KeysignedKey' + keyId, keyPair));
},
removeSignedPreKey: function(keyId) {
return Promise.resolve(this.remove('25519KeysignedKey' + keyId));
},
},
storeSignedPreKey: function(keyId, keyPair) {
return Promise.resolve(this.put('25519KeysignedKey' + keyId, keyPair));
},
removeSignedPreKey: function(keyId) {
return Promise.resolve(this.remove('25519KeysignedKey' + keyId));
},
loadSession: function(identifier) {
return Promise.resolve(this.get('session' + identifier));
},
storeSession: function(identifier, record) {
return Promise.resolve(this.put('session' + identifier, record));
},
loadSession: function(identifier) {
return Promise.resolve(this.get('session' + identifier));
},
storeSession: function(identifier, record) {
return Promise.resolve(this.put('session' + identifier, record));
},
removeSession: function(identifier) {
return Promise.resolve(this.remove('session' + identifier));
return Promise.resolve(this.remove('session' + identifier));
},
removeAllSessions: function(identifier) {
for (var id in this.store) {

View File

@ -1,5 +1,51 @@
describe('SessionBuilder', function() {
this.timeout(5000);
var KeyHelper = libsignal.KeyHelper;
function generateIdentity(store) {
return Promise.all([
KeyHelper.generateIdentityKeyPair(),
KeyHelper.generateRegistrationId(),
]).then(function(result) {
store.put('identityKey', result[0]);
store.put('registrationId', result[1]);
});
}
function generatePreKeyBundle(store, preKeyId, signedPreKeyId) {
return Promise.all([
store.getIdentityKeyPair(),
store.getLocalRegistrationId()
]).then(function(result) {
var identity = result[0];
var registrationId = result[1];
return Promise.all([
KeyHelper.generatePreKey(preKeyId),
KeyHelper.generateSignedPreKey(identity, signedPreKeyId),
]).then(function(keys) {
var preKey = keys[0]
var signedPreKey = keys[1];
store.storePreKey(preKeyId, preKey.keyPair);
store.storeSignedPreKey(signedPreKeyId, signedPreKey.keyPair);
return {
identityKey: identity.pubKey,
registrationId : registrationId,
preKey: {
keyId : preKeyId,
publicKey : preKey.keyPair.pubKey
},
signedPreKey: {
keyId : signedPreKeyId,
publicKey : signedPreKey.keyPair.pubKey,
signature : signedPreKey.signature
}
};
});
});
}
var ALICE_ADDRESS = new SignalProtocolAddress("+14151111111", 1);
var BOB_ADDRESS = new SignalProtocolAddress("+14152222222", 1);
@ -96,99 +142,4 @@ describe('SessionBuilder', function() {
});
});
});
describe("basic v3 NO PREKEY", function() {
var aliceStore = new SignalProtocolStore();
var bobStore = new SignalProtocolStore();
var bobPreKeyId = 1337;
var bobSignedKeyId = 1;
var Curve = libsignal.Curve;
before(function(done) {
Promise.all([
generateIdentity(aliceStore),
generateIdentity(bobStore),
]).then(function() {
return generatePreKeyBundle(bobStore, bobPreKeyId, bobSignedKeyId);
}).then(function(preKeyBundle) {
delete preKeyBundle.preKey;
var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS);
return builder.processPreKey(preKeyBundle).then(function() {
done();
});
}).catch(done);
});
var originalMessage = util.toArrayBuffer("L'homme est condamné à être libre");
var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS);
var bobSessionCipher = new libsignal.SessionCipher(bobStore, ALICE_ADDRESS);
it('creates a session', function(done) {
return aliceStore.loadSession(BOB_ADDRESS.toString()).then(function(record) {
assert.isDefined(record);
var sessionRecord = Internal.SessionRecord.deserialize(record);
assert.isTrue(sessionRecord.haveOpenSession());
assert.isDefined(sessionRecord.getOpenSession());
}).then(done, done);
});
it('the session can encrypt', function(done) {
aliceSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
assert.strictEqual(ciphertext.type, 3); // PREKEY_BUNDLE
return bobSessionCipher.decryptPreKeyWhisperMessage(ciphertext.body, 'binary');
}).then(function(plaintext) {
assertEqualArrayBuffers(plaintext, originalMessage);
}).then(done, done);
});
it('the session can decrypt', function(done) {
bobSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
return aliceSessionCipher.decryptWhisperMessage(ciphertext.body, 'binary');
}).then(function(plaintext) {
assertEqualArrayBuffers(plaintext, originalMessage);
}).then(done, done);
});
it('accepts a new preKey with the same identity', function(done) {
generatePreKeyBundle(bobStore, bobPreKeyId + 1, bobSignedKeyId + 1).then(function(preKeyBundle) {
delete preKeyBundle.preKey;
var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS);
return builder.processPreKey(preKeyBundle).then(function() {
return aliceStore.loadSession(BOB_ADDRESS.toString()).then(function(record) {
assert.isDefined(record);
var sessionRecord = Internal.SessionRecord.deserialize(record);
assert.isTrue(sessionRecord.haveOpenSession());
assert.isDefined(sessionRecord.getOpenSession());
done();
});
});
}).catch(done);
});
it('rejects untrusted identity keys', function(done) {
KeyHelper.generateIdentityKeyPair().then(function(newIdentity) {
var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS);
return builder.processPreKey({
identityKey: newIdentity.pubKey,
registrationId : 12356
}).then(function(e) {
assert.fail('should not be trusted');
}).catch(function(e) {
assert.strictEqual(e.message, 'Identity key changed');
done();
}).catch(done);
});
});
});
});

View File

@ -365,68 +365,4 @@ describe('SessionCipher', function() {
});
});
});
describe("key changes", function() {
var ALICE_ADDRESS = new SignalProtocolAddress("+14151111111", 1);
var BOB_ADDRESS = new SignalProtocolAddress("+14152222222", 1);
var originalMessage = util.toArrayBuffer("L'homme est condamné à être libre");
var aliceStore = new SignalProtocolStore();
var bobStore = new SignalProtocolStore();
var bobPreKeyId = 1337;
var bobSignedKeyId = 1;
var Curve = libsignal.Curve;
var bobSessionCipher = new libsignal.SessionCipher(bobStore, ALICE_ADDRESS);
before(function(done) {
Promise.all(
[aliceStore, bobStore].map(generateIdentity)
).then(function() {
return generatePreKeyBundle(bobStore, bobPreKeyId, bobSignedKeyId);
}).then(function(preKeyBundle) {
var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS);
return builder.processPreKey(preKeyBundle).then(function() {
var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS);
return aliceSessionCipher.encrypt(originalMessage);
}).then(function(ciphertext) {
return bobSessionCipher.decryptPreKeyWhisperMessage(ciphertext.body, 'binary');
}).then(function() {
done();
});
}).catch(done);
});
describe("When bob's identity changes", function() {
var messageFromBob;
before(function(done) {
return bobSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
messageFromBob = ciphertext;
}).then(function() {
return generateIdentity(bobStore);
}).then(function() {
return aliceStore.saveIdentity(BOB_ADDRESS.toString(), bobStore.get('identityKey').pubKey);
}).then(function() {
done();
});
});
it('alice cannot encrypt with the old session', function(done) {
var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS);
return aliceSessionCipher.encrypt(originalMessage).catch(function(e) {
assert.strictEqual(e.message, 'Identity key changed');
}).then(done,done);
});
it('alice cannot decrypt from the old session', function(done) {
var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS);
return aliceSessionCipher.decryptWhisperMessage(messageFromBob.body, 'binary').catch(function(e) {
assert.strictEqual(e.message, 'Identity key changed');
}).then(done, done);
});
});
});
});

View File

@ -52,50 +52,3 @@ function hexToArrayBuffer(str) {
array[i] = parseInt(str.substr(i*2, 2), 16);
return ret;
};
var KeyHelper = libsignal.KeyHelper;
function generateIdentity(store) {
return Promise.all([
KeyHelper.generateIdentityKeyPair(),
KeyHelper.generateRegistrationId(),
]).then(function(result) {
store.put('identityKey', result[0]);
store.put('registrationId', result[1]);
});
}
function generatePreKeyBundle(store, preKeyId, signedPreKeyId) {
return Promise.all([
store.getIdentityKeyPair(),
store.getLocalRegistrationId()
]).then(function(result) {
var identity = result[0];
var registrationId = result[1];
return Promise.all([
KeyHelper.generatePreKey(preKeyId),
KeyHelper.generateSignedPreKey(identity, signedPreKeyId),
]).then(function(keys) {
var preKey = keys[0]
var signedPreKey = keys[1];
store.storePreKey(preKeyId, preKey.keyPair);
store.storeSignedPreKey(signedPreKeyId, signedPreKey.keyPair);
return {
identityKey: identity.pubKey,
registrationId : registrationId,
preKey: {
keyId : preKeyId,
publicKey : preKey.keyPair.pubKey
},
signedPreKey: {
keyId : signedPreKeyId,
publicKey : signedPreKey.keyPair.pubKey,
signature : signedPreKey.signature
}
};
});
});
}

View File

@ -34453,50 +34453,3 @@ function hexToArrayBuffer(str) {
array[i] = parseInt(str.substr(i*2, 2), 16);
return ret;
};
var KeyHelper = libsignal.KeyHelper;
function generateIdentity(store) {
return Promise.all([
KeyHelper.generateIdentityKeyPair(),
KeyHelper.generateRegistrationId(),
]).then(function(result) {
store.put('identityKey', result[0]);
store.put('registrationId', result[1]);
});
}
function generatePreKeyBundle(store, preKeyId, signedPreKeyId) {
return Promise.all([
store.getIdentityKeyPair(),
store.getLocalRegistrationId()
]).then(function(result) {
var identity = result[0];
var registrationId = result[1];
return Promise.all([
KeyHelper.generatePreKey(preKeyId),
KeyHelper.generateSignedPreKey(identity, signedPreKeyId),
]).then(function(keys) {
var preKey = keys[0]
var signedPreKey = keys[1];
store.storePreKey(preKeyId, preKey.keyPair);
store.storeSignedPreKey(signedPreKeyId, signedPreKey.keyPair);
return {
identityKey: identity.pubKey,
registrationId : registrationId,
preKey: {
keyId : preKeyId,
publicKey : preKey.keyPair.pubKey
},
signedPreKey: {
keyId : signedPreKeyId,
publicKey : signedPreKey.keyPair.pubKey,
signature : signedPreKey.signature
}
};
});
});
}