Compare commits

...

43 Commits

Author SHA1 Message Date
jimio-signal
4fbea5a8f8
Update README.md 2021-08-04 14:06:48 -07:00
Scott Nonnenberg
6a5805234f
Merge pull request #72 from dag0310/dag0310-patch-1
Fix typo in README.md
2021-01-12 17:33:34 -08:00
Daniel Geymayer
de6728487e
Fix typo in README.md 2020-04-25 00:39:47 +02:00
Scott Nonnenberg
f5a838f1cc
Update copyright year to 2018 on README.md (#47)
Update copyright year to 2018 on README.md
2018-05-04 10:30:18 -07:00
Nick Weingartner
af5f9653d6 Update copyright year to 2018 2018-05-04 10:04:46 -04:00
Scott Nonnenberg
d6e55c2e33
v1.3.0
Remove 1000 messageKey limit entirely, along with custom limits (#42)
Throw error if asked to jump forward in chain by over 2000 (#43)

Remove logging from verifyMAC on mismatch (#44)

Update to new signalapp github organization (#45)
Fix code example typo in Readme (#39)
2018-02-14 11:42:55 -08:00
Scott Nonnenberg
a80285cc83
Remove 1000 messageKey limit entirely, along with custom limits (#42) 2018-02-14 11:35:41 -08:00
Scott Nonnenberg
d08705329a
Throw error if asked to jump forward in chain by over 2000 (#43)
As libsignal-protocol-java has had for more than three years. This check
was present when it was first pulled out into its own repo in late 2014:

60800e1556/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java (L383)
2018-02-14 11:35:16 -08:00
Scott Nonnenberg
5679fe7a49
Remove logging from verifyMAC on mismatch (#44)
We don't use it when debugging anyway.
2018-02-14 11:34:28 -08:00
Scott Nonnenberg
c95f5882a9
Update to new signalapp github organization (#45) 2018-02-14 11:34:17 -08:00
Yasin Okumus
b5514743fb Fix code example typo in Readme #38 (#39) 2017-12-18 14:33:41 -08:00
Scott Nonnenberg
cd2042f9ce
Built assets catch-up 2017-11-20 17:56:58 -08:00
Scott Nonnenberg
a39e38038a
v1.2.0
Quite a few changes to reflect new behavior around identity keys rolled
out in the Signal apps in June 2017:
  - We always trust a new identity key when receiving messages
  - We have a few requirements for trust when sending messages:
    1) it can't have changed very recently
    2) if the previous identity key had been verified by the user, the
       user needs to explicitly approve a new identity key

Expose ability to delete all existing sessions - for proper reset (#35)

Allow caller to provide fillMessageKeys limit - allowing customization
  of the maxiumum number of one-sided messages in a conversation (#29)

Dev:
  - A number of changes to make the branch build in CI, add badge, etc.

Note: we appear to have some unreliability during our Firefox 34 builds
on Sauce. A little less than half the time the test fails with an out of
memory error.
2017-11-20 17:32:46 -08:00
Scott Nonnenberg
f1e22f66a3
Expose ability to fully delete existing sessions - for resets (#35) 2017-11-20 17:19:23 -08:00
Scott Nonnenberg
6154353cd9
Merge branch 'trust-store-changes'
Catching master up with the latest updates. the trust-store-changes branch has all
recent tags, and the master branch has a few readme and project-level changes.
2017-11-20 16:55:26 -08:00
Scott Nonnenberg
dbf8c26967
Update grunt-contrib-jshint to make build succeed 2017-11-06 12:15:57 -08:00
Scott Nonnenberg
beb11f0292
Travis: Update to node 6 2017-11-06 12:07:28 -08:00
Frank Cash
70eb5c5494 Add Travis-CI badge (#20) 2017-11-06 12:01:50 -08:00
Scott Nonnenberg
5d579e93fc
Merge pull request #19 from frankcash/patch-1
Update README.md
2017-11-06 11:56:17 -08:00
Scott Nonnenberg
3d72df72c0 Merge pull request #29 from WhisperSystems/unlimited-for-yourself
fillMessageKeys: allow for SessionCipher creator to specify limit
2017-08-04 11:26:54 -07:00
Scott Nonnenberg
b6c3093945
SessionCipher: allow caller to provide fillMessageKeys limit
If options.messageKeysLimit is provided by falsey, then we don't apply
any limit at all. This can be used to set no limit for communications
from your own devices.

Why would you want that? People leave their laptops closed for weeks at
a time and get this error, since their other devices are sending
messages to it constantly:

"Too many message keys for chain"

And it seems to be really hard to fix once you're in this state. Sync
messages no longer show up from the device that got into this state.

FREEBIE
2017-08-04 10:50:31 -07:00
lilia
f308236941 Fix tests
Update in-memory storage implementation to parse full address strings in
saveIdentity. Update usage of saveIdentity, passing in the full address string.
2017-07-02 16:12:07 -10:00
lilia
1fe18c3384 Always pass the full address to saveIdentity
For consistency, and so the storage interface implementation can make descisions
about archiving sibling sessions.
2017-06-27 09:43:40 -10:00
lilia
b65f69af6e Fix direction in decrypt check 2017-06-01 15:08:37 -07:00
lilia
cee3b82317 Remove removePreviousSessions
This reverts commit b45ace4 and parts of 2657f00.

The purpose of the deletes was so that a session with a different identity key
couldn't get "resurrected" after an identity switch. But if the validate/save
action is happening for every encrypt/decrypt, then it shouldn't be possible
for an old identity to get invisibly resurrected.

Furthermore, at the application level we would clear sessions for all devices
on the affected number, but libsignal-protocol operates on a per-device basis,
so it can only perform what amounts to an incomplete removal.
2017-05-30 14:54:27 -07:00
lilia
e68e17c861 Test SessionCipher for encrypt/decrypt identity checks
In order to facilitate generation of session state in these tests, move some
helpers from SessionBuilderTest.js into global namespace of the test page
2017-05-30 14:54:27 -07:00
Scott Nonnenberg
2657f00c98 Add identity checks to encrypt/decrypt
WhisperSystems/libsignal-protocol-java@6935c70081
2017-05-26 14:27:52 -07:00
lilia
b45ace4d58 Clear old sessions for a device when its identity changes 2017-05-26 14:27:52 -07:00
lilia
ee00f080f4 Resolve true if saveIdentity overwrote a key
Reflect whether a saveIdentity operation reflects a replacement of an existing
key or not. The return value can be used by the caller to decide whether they
should clear sessions for the recipient.
2017-05-26 14:26:35 -07:00
lilia
63d2536263 Support for directional trust
Pass in a direction to isTrusted, based on constants defined by the storage
implementation. These values allow the trust store to determine whether a given
key is trusted, given the directional context.
2017-05-26 14:23:38 -07:00
lilia
b0ed2e5e4f Fix check for session equivalence
This check didn't work, which resulted in closing and then immediately
reopening the current session. Not that big a deal, since it merely
flips a flag off and back on again just before saving the session
record, but does result in spurious log statements.
2017-04-12 11:54:11 -07:00
Frank Cash
3607679e93 Update README.md 2017-04-04 22:17:49 -04:00
lilia
b77842c30e Promote old session states 2017-03-27 19:17:33 -07:00
lilia
b01ac22eb9 Fix bad mac on repeated deliveries
`MessageCounterError` is the expected result of repeated deliveries.
Error handling in decryptWithSessionList should immediately return such
an error if it occurs. Previous to this change, a Bad MAC error to be
returned instead of a MessageCounterError, if the message in question
was encrypted for a closed session.
2017-03-27 17:01:56 -07:00
lilia
3c0d6dc24b Normalize whitespace 2017-03-05 20:36:26 -08:00
lilia
963a424451 Fix test for presence of registrationId 2017-02-20 18:08:15 -08:00
lilia
17fdde0091 Remove unused argument 2017-02-20 18:08:04 -08:00
lilia
538c7aecce Fix crashes when no preKey is present
Adds a test identical to the 'basic prekey v3' test with the addition of
`delete preKeyBundle.preKey` where appropriate.
2017-02-09 10:53:53 -08:00
lilia
8334fa5699 Double the max old ratchet list length
Attempt to mitigate Bad MAC errors due to repeated ciphertext from more
than 5 exchanges ago.
2016-11-16 21:29:39 +01:00
lilia
7bc356ac15 Rename variable 2016-11-16 15:57:59 +01:00
lilia
861fc80580 Open sessions must have registrationIds 2016-10-05 21:57:01 +09:00
lilia
8df967ab64 Debug logging on migration failures 2016-10-05 21:56:15 +09:00
lilia
34eeb93470 Better debug output when JSON encoding fails 2016-10-05 21:22:25 +09:00
15 changed files with 709 additions and 418 deletions

View File

@ -1,10 +1,10 @@
language: node_js
node_js:
- '0.10'
- '6'
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,7 +1,12 @@
#libsignal-protocol-javascript
**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)
Signal Protocol implementation for the browser based on
[libsignal-protocol-java](https://github.com/WhisperSystems/libsignal-protocol-java).
[libsignal-protocol-java](https://github.com/signalapp/libsignal-protocol-java).
```
/dist # Distributables
@ -112,7 +117,7 @@ var store = new MySignalProtocolStore();
var address = new libsignal.SignalProtocolAddress(recipientId, deviceId);
// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
SessionBuilder sessionBuilder = new libsignal.SessionBuilder(store, address);
var 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
@ -181,7 +186,7 @@ sessionCipher.decryptWhisperMessage(ciphertext).then(function(plaintext) {
## Building
To compile curve25519 from C souce files in `/native`, install
To compile curve25519 from C source files in `/native`, install
[emscripten](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
```
@ -190,6 +195,6 @@ grunt compile
## License
Copyright 2015-2016 Open Whisper Systems
Copyright 2015-2018 Open Whisper Systems
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html

View File

@ -47,21 +47,18 @@
/**
* 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;
}
@ -87,10 +84,9 @@
* An indicator used to reliably determine if an object is a Long or not.
* @type {boolean}
* @const
* @expose
* @private
*/
Long.__isLong__;
Long.prototype.__isLong__;
Object.defineProperty(Long.prototype, "__isLong__", {
value: true,
@ -113,7 +109,6 @@
* @function
* @param {*} obj Object
* @returns {boolean}
* @expose
*/
Long.isLong = isLong;
@ -170,7 +165,6 @@
* @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;
@ -205,7 +199,6 @@
* @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;
@ -228,7 +221,6 @@
* @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;
@ -298,7 +290,6 @@
* @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;
@ -324,7 +315,6 @@
* @function
* @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value
* @returns {!Long}
* @expose
*/
Long.fromValue = fromValue;
@ -382,7 +372,6 @@
/**
* Signed zero.
* @type {!Long}
* @expose
*/
Long.ZERO = ZERO;
@ -395,7 +384,6 @@
/**
* Unsigned zero.
* @type {!Long}
* @expose
*/
Long.UZERO = UZERO;
@ -408,7 +396,6 @@
/**
* Signed one.
* @type {!Long}
* @expose
*/
Long.ONE = ONE;
@ -421,7 +408,6 @@
/**
* Unsigned one.
* @type {!Long}
* @expose
*/
Long.UONE = UONE;
@ -434,7 +420,6 @@
/**
* Signed negative one.
* @type {!Long}
* @expose
*/
Long.NEG_ONE = NEG_ONE;
@ -447,7 +432,6 @@
/**
* Maximum signed value.
* @type {!Long}
* @expose
*/
Long.MAX_VALUE = MAX_VALUE;
@ -460,7 +444,6 @@
/**
* Maximum unsigned value.
* @type {!Long}
* @expose
*/
Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE;
@ -473,7 +456,6 @@
/**
* Minimum signed value.
* @type {!Long}
* @expose
*/
Long.MIN_VALUE = MIN_VALUE;
@ -486,7 +468,6 @@
/**
* 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;
@ -495,7 +476,6 @@
/**
* 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)
@ -509,7 +489,6 @@
* @returns {string}
* @override
* @throws {RangeError} If `radix` is out of range
* @expose
*/
LongPrototype.toString = function toString(radix) {
radix = radix || 10;
@ -552,7 +531,6 @@
/**
* Gets the high 32 bits as a signed integer.
* @returns {number} Signed high bits
* @expose
*/
LongPrototype.getHighBits = function getHighBits() {
return this.high;
@ -561,7 +539,6 @@
/**
* Gets the high 32 bits as an unsigned integer.
* @returns {number} Unsigned high bits
* @expose
*/
LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() {
return this.high >>> 0;
@ -570,7 +547,6 @@
/**
* Gets the low 32 bits as a signed integer.
* @returns {number} Signed low bits
* @expose
*/
LongPrototype.getLowBits = function getLowBits() {
return this.low;
@ -579,7 +555,6 @@
/**
* Gets the low 32 bits as an unsigned integer.
* @returns {number} Unsigned low bits
* @expose
*/
LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() {
return this.low >>> 0;
@ -588,7 +563,6 @@
/**
* 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
@ -603,7 +577,6 @@
/**
* Tests if this Long's value equals zero.
* @returns {boolean}
* @expose
*/
LongPrototype.isZero = function isZero() {
return this.high === 0 && this.low === 0;
@ -612,7 +585,6 @@
/**
* Tests if this Long's value is negative.
* @returns {boolean}
* @expose
*/
LongPrototype.isNegative = function isNegative() {
return !this.unsigned && this.high < 0;
@ -621,7 +593,6 @@
/**
* Tests if this Long's value is positive.
* @returns {boolean}
* @expose
*/
LongPrototype.isPositive = function isPositive() {
return this.unsigned || this.high >= 0;
@ -630,7 +601,6 @@
/**
* Tests if this Long's value is odd.
* @returns {boolean}
* @expose
*/
LongPrototype.isOdd = function isOdd() {
return (this.low & 1) === 1;
@ -639,7 +609,6 @@
/**
* Tests if this Long's value is even.
* @returns {boolean}
* @expose
*/
LongPrototype.isEven = function isEven() {
return (this.low & 1) === 0;
@ -649,7 +618,6 @@
* 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))
@ -664,7 +632,6 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.eq = LongPrototype.equals;
@ -672,7 +639,6 @@
* 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);
@ -683,7 +649,6 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.neq = LongPrototype.notEquals;
@ -691,7 +656,6 @@
* 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;
@ -702,7 +666,6 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lt = LongPrototype.lessThan;
@ -710,7 +673,6 @@
* 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;
@ -721,7 +683,6 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lte = LongPrototype.lessThanOrEqual;
@ -729,7 +690,6 @@
* 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;
@ -740,7 +700,6 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gt = LongPrototype.greaterThan;
@ -748,7 +707,6 @@
* 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;
@ -759,7 +717,6 @@
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gte = LongPrototype.greaterThanOrEqual;
@ -768,7 +725,6 @@
* @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))
@ -794,14 +750,12 @@
* @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))
@ -813,7 +767,6 @@
* Negates this Long's value. This is an alias of {@link Long#negate}.
* @function
* @returns {!Long} Negated Long
* @expose
*/
LongPrototype.neg = LongPrototype.negate;
@ -821,7 +774,6 @@
* 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))
@ -858,7 +810,6 @@
* 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))
@ -871,7 +822,6 @@
* @function
* @param {!Long|number|string} subtrahend Subtrahend
* @returns {!Long} Difference
* @expose
*/
LongPrototype.sub = LongPrototype.subtract;
@ -879,7 +829,6 @@
* 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())
@ -947,7 +896,6 @@
* @function
* @param {!Long|number|string} multiplier Multiplier
* @returns {!Long} Product
* @expose
*/
LongPrototype.mul = LongPrototype.multiply;
@ -956,7 +904,6 @@
* unsigned if this Long is unsigned.
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.divide = function divide(divisor) {
if (!isLong(divisor))
@ -967,6 +914,8 @@
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
@ -992,19 +941,18 @@
return this.neg().div(divisor).neg();
} else if (divisor.isNegative())
return this.div(divisor.neg()).neg();
} 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) {
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();
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
@ -1048,7 +996,6 @@
* @function
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.div = LongPrototype.divide;
@ -1056,7 +1003,6 @@
* Returns this Long modulo the specified.
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Remainder
* @expose
*/
LongPrototype.modulo = function modulo(divisor) {
if (!isLong(divisor))
@ -1069,14 +1015,12 @@
* @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);
@ -1086,7 +1030,6 @@
* 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))
@ -1098,7 +1041,6 @@
* 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))
@ -1110,7 +1052,6 @@
* 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))
@ -1122,7 +1063,6 @@
* 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))
@ -1140,7 +1080,6 @@
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shl = LongPrototype.shiftLeft;
@ -1148,7 +1087,6 @@
* 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))
@ -1166,7 +1104,6 @@
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shr = LongPrototype.shiftRight;
@ -1174,7 +1111,6 @@
* 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))
@ -1199,14 +1135,12 @@
* @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)
@ -1217,7 +1151,6 @@
/**
* Converts this Long to unsigned.
* @returns {!Long} Unsigned long
* @expose
*/
LongPrototype.toUnsigned = function toUnsigned() {
if (this.unsigned)
@ -1225,6 +1158,53 @@
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,21 +25399,18 @@ 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;
}
@ -25439,10 +25436,9 @@ Curve25519Worker.prototype = {
* An indicator used to reliably determine if an object is a Long or not.
* @type {boolean}
* @const
* @expose
* @private
*/
Long.__isLong__;
Long.prototype.__isLong__;
Object.defineProperty(Long.prototype, "__isLong__", {
value: true,
@ -25465,7 +25461,6 @@ Curve25519Worker.prototype = {
* @function
* @param {*} obj Object
* @returns {boolean}
* @expose
*/
Long.isLong = isLong;
@ -25522,7 +25517,6 @@ 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;
@ -25557,7 +25551,6 @@ 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;
@ -25580,7 +25573,6 @@ 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;
@ -25650,7 +25642,6 @@ 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;
@ -25676,7 +25667,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value
* @returns {!Long}
* @expose
*/
Long.fromValue = fromValue;
@ -25734,7 +25724,6 @@ Curve25519Worker.prototype = {
/**
* Signed zero.
* @type {!Long}
* @expose
*/
Long.ZERO = ZERO;
@ -25747,7 +25736,6 @@ Curve25519Worker.prototype = {
/**
* Unsigned zero.
* @type {!Long}
* @expose
*/
Long.UZERO = UZERO;
@ -25760,7 +25748,6 @@ Curve25519Worker.prototype = {
/**
* Signed one.
* @type {!Long}
* @expose
*/
Long.ONE = ONE;
@ -25773,7 +25760,6 @@ Curve25519Worker.prototype = {
/**
* Unsigned one.
* @type {!Long}
* @expose
*/
Long.UONE = UONE;
@ -25786,7 +25772,6 @@ Curve25519Worker.prototype = {
/**
* Signed negative one.
* @type {!Long}
* @expose
*/
Long.NEG_ONE = NEG_ONE;
@ -25799,7 +25784,6 @@ Curve25519Worker.prototype = {
/**
* Maximum signed value.
* @type {!Long}
* @expose
*/
Long.MAX_VALUE = MAX_VALUE;
@ -25812,7 +25796,6 @@ Curve25519Worker.prototype = {
/**
* Maximum unsigned value.
* @type {!Long}
* @expose
*/
Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE;
@ -25825,7 +25808,6 @@ Curve25519Worker.prototype = {
/**
* Minimum signed value.
* @type {!Long}
* @expose
*/
Long.MIN_VALUE = MIN_VALUE;
@ -25838,7 +25820,6 @@ 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;
@ -25847,7 +25828,6 @@ 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)
@ -25861,7 +25841,6 @@ Curve25519Worker.prototype = {
* @returns {string}
* @override
* @throws {RangeError} If `radix` is out of range
* @expose
*/
LongPrototype.toString = function toString(radix) {
radix = radix || 10;
@ -25904,7 +25883,6 @@ Curve25519Worker.prototype = {
/**
* Gets the high 32 bits as a signed integer.
* @returns {number} Signed high bits
* @expose
*/
LongPrototype.getHighBits = function getHighBits() {
return this.high;
@ -25913,7 +25891,6 @@ 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;
@ -25922,7 +25899,6 @@ Curve25519Worker.prototype = {
/**
* Gets the low 32 bits as a signed integer.
* @returns {number} Signed low bits
* @expose
*/
LongPrototype.getLowBits = function getLowBits() {
return this.low;
@ -25931,7 +25907,6 @@ 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;
@ -25940,7 +25915,6 @@ 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
@ -25955,7 +25929,6 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value equals zero.
* @returns {boolean}
* @expose
*/
LongPrototype.isZero = function isZero() {
return this.high === 0 && this.low === 0;
@ -25964,7 +25937,6 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is negative.
* @returns {boolean}
* @expose
*/
LongPrototype.isNegative = function isNegative() {
return !this.unsigned && this.high < 0;
@ -25973,7 +25945,6 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is positive.
* @returns {boolean}
* @expose
*/
LongPrototype.isPositive = function isPositive() {
return this.unsigned || this.high >= 0;
@ -25982,7 +25953,6 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is odd.
* @returns {boolean}
* @expose
*/
LongPrototype.isOdd = function isOdd() {
return (this.low & 1) === 1;
@ -25991,7 +25961,6 @@ Curve25519Worker.prototype = {
/**
* Tests if this Long's value is even.
* @returns {boolean}
* @expose
*/
LongPrototype.isEven = function isEven() {
return (this.low & 1) === 0;
@ -26001,7 +25970,6 @@ 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))
@ -26016,7 +25984,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.eq = LongPrototype.equals;
@ -26024,7 +25991,6 @@ 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);
@ -26035,7 +26001,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.neq = LongPrototype.notEquals;
@ -26043,7 +26008,6 @@ 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;
@ -26054,7 +26018,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lt = LongPrototype.lessThan;
@ -26062,7 +26025,6 @@ 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;
@ -26073,7 +26035,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.lte = LongPrototype.lessThanOrEqual;
@ -26081,7 +26042,6 @@ 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;
@ -26092,7 +26052,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gt = LongPrototype.greaterThan;
@ -26100,7 +26059,6 @@ 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;
@ -26111,7 +26069,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} other Other value
* @returns {boolean}
* @expose
*/
LongPrototype.gte = LongPrototype.greaterThanOrEqual;
@ -26120,7 +26077,6 @@ 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))
@ -26146,14 +26102,12 @@ 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))
@ -26165,7 +26119,6 @@ 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;
@ -26173,7 +26126,6 @@ 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))
@ -26210,7 +26162,6 @@ 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))
@ -26223,7 +26174,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} subtrahend Subtrahend
* @returns {!Long} Difference
* @expose
*/
LongPrototype.sub = LongPrototype.subtract;
@ -26231,7 +26181,6 @@ 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())
@ -26299,7 +26248,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} multiplier Multiplier
* @returns {!Long} Product
* @expose
*/
LongPrototype.mul = LongPrototype.multiply;
@ -26308,7 +26256,6 @@ 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))
@ -26319,6 +26266,8 @@ 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
@ -26344,19 +26293,18 @@ Curve25519Worker.prototype = {
return this.neg().div(divisor).neg();
} else if (divisor.isNegative())
return this.div(divisor.neg()).neg();
} 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) {
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();
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
@ -26400,7 +26348,6 @@ Curve25519Worker.prototype = {
* @function
* @param {!Long|number|string} divisor Divisor
* @returns {!Long} Quotient
* @expose
*/
LongPrototype.div = LongPrototype.divide;
@ -26408,7 +26355,6 @@ 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))
@ -26421,14 +26367,12 @@ 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);
@ -26438,7 +26382,6 @@ 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))
@ -26450,7 +26393,6 @@ 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))
@ -26462,7 +26404,6 @@ 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))
@ -26474,7 +26415,6 @@ 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))
@ -26492,7 +26432,6 @@ Curve25519Worker.prototype = {
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shl = LongPrototype.shiftLeft;
@ -26500,7 +26439,6 @@ 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))
@ -26518,7 +26456,6 @@ Curve25519Worker.prototype = {
* @function
* @param {number|!Long} numBits Number of bits
* @returns {!Long} Shifted Long
* @expose
*/
LongPrototype.shr = LongPrototype.shiftRight;
@ -26526,7 +26463,6 @@ 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))
@ -26551,14 +26487,12 @@ 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)
@ -26569,7 +26503,6 @@ Curve25519Worker.prototype = {
/**
* Converts this Long to unsigned.
* @returns {!Long} Unsigned long
* @expose
*/
LongPrototype.toUnsigned = function toUnsigned() {
if (this.unsigned)
@ -26577,6 +26510,53 @@ 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;
});
@ -35311,8 +35291,6 @@ 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");
}
});
@ -35515,6 +35493,7 @@ 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__;
@ -35541,7 +35520,12 @@ Internal.SessionRecord = function() {
} else if (thing === Object(thing)) {
var obj = {};
for (var key in thing) {
obj[key] = ensureStringed(thing[key]);
try {
obj[key] = ensureStringed(thing[key]);
} catch (ex) {
console.log('Error serializing key', key);
throw ex;
}
}
return obj;
} else if (thing === null) {
@ -35560,12 +35544,21 @@ Internal.SessionRecord = function() {
version: 'v1',
migrate: function migrateV1(data) {
var sessions = data.sessions;
var key;
if (data.registrationId) {
for (var key in sessions) {
for (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);
}
}
}
}
}
@ -35586,7 +35579,7 @@ Internal.SessionRecord = function() {
}
var SessionRecord = function() {
this._sessions = {};
this.sessions = {};
this.version = SESSION_RECORD_VERSION;
};
@ -35595,8 +35588,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;
@ -35605,16 +35598,17 @@ Internal.SessionRecord = function() {
SessionRecord.prototype = {
serialize: function() {
return jsonThing({
sessions : this._sessions,
sessions : this.sessions,
version : this.version
});
},
haveOpenSession: function() {
return this.getOpenSession() !== undefined;
var openSession = this.getOpenSession();
return (!!openSession && typeof openSession.registrationId === 'number');
},
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;
@ -35623,7 +35617,7 @@ Internal.SessionRecord = function() {
},
getSessionByRemoteEphemeralKey: function(remoteEphemeralKey) {
this.detectDuplicateOpenSessions();
var sessions = this._sessions;
var sessions = this.sessions;
var searchKey = util.toString(remoteEphemeralKey);
@ -35643,7 +35637,7 @@ Internal.SessionRecord = function() {
return undefined;
},
getOpenSession: function() {
var sessions = this._sessions;
var sessions = this.sessions;
if (sessions === undefined) {
return undefined;
}
@ -35659,7 +35653,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) {
@ -35670,7 +35664,7 @@ Internal.SessionRecord = function() {
}
},
updateSessionState: function(session) {
var sessions = this._sessions;
var sessions = this.sessions;
this.removeOldChains(session);
@ -35684,11 +35678,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) {
@ -35702,38 +35696,20 @@ Internal.SessionRecord = function() {
archiveCurrentState: function() {
var open_session = this.getOpenSession();
if (open_session !== undefined) {
this.closeSession(open_session);
console.log('closing session');
open_session.indexInfo.closed = Date.now();
this.updateSessionState(open_session);
}
},
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);
promoteState: function(session) {
console.log('promoting session');
session.indexInfo.closed = -1;
},
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 five.
while (session.oldRatchetList.length > 5) {
// here and remove all but the last ten.
while (session.oldRatchetList.length > OLD_RATCHETS_MAX_LENGTH) {
var index = 0;
var oldest = session.oldRatchetList[0];
for (var i = 0; i < session.oldRatchetList.length; i++) {
@ -35749,7 +35725,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) {
@ -35764,6 +35740,10 @@ 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;
@ -35815,7 +35795,7 @@ SessionBuilder.prototype = {
processPreKey: function(device) {
return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), device.identityKey
this.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
@ -35829,15 +35809,20 @@ SessionBuilder.prototype = {
}).then(function() {
return Internal.crypto.createKeyPair();
}).then(function(baseKey) {
var devicePreKey = (device.preKey.publicKey);
var devicePreKey;
if (device.preKey) {
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) {
@ -35851,10 +35836,10 @@ SessionBuilder.prototype = {
}
record.archiveCurrentState();
record.updateSessionState(session, device.registrationId);
record.updateSessionState(session);
return Promise.all([
this.storage.storeSession(address, record.serialize()),
this.storage.saveIdentity(this.remoteAddress.getName(), session.indexInfo.remoteIdentityKey)
this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey)
]);
}.bind(this));
}.bind(this));
@ -35863,7 +35848,7 @@ SessionBuilder.prototype = {
processV3: function(record, message) {
var preKeyPair, signedPreKeyPair, session;
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), message.identityKey.toArrayBuffer()
this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING
).then(function(trusted) {
if (!trusted) {
var e = new Error('Unknown identity key');
@ -35910,8 +35895,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, message.registrationId);
return this.storage.saveIdentity(this.remoteAddress.getName(), message.identityKey.toArrayBuffer()).then(function() {
record.updateSessionState(new_session);
return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() {
return message.preKeyId;
});
}.bind(this));
@ -36107,10 +36092,20 @@ SessionCipher.prototype = {
result.set(new Uint8Array(encodedMsg), 1);
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result;
});
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));
}.bind(this));
}.bind(this));
}.bind(this)).then(function(message) {
@ -36120,7 +36115,9 @@ SessionCipher.prototype = {
preKeyMsg.registrationId = myRegistrationId;
preKeyMsg.baseKey = util.toArrayBuffer(session.pendingPreKey.baseKey);
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
if (session.pendingPreKey.preKeyId) {
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
}
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
preKeyMsg.message = message;
@ -36153,6 +36150,10 @@ 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));
@ -36168,10 +36169,25 @@ SessionCipher.prototype = {
var errors = [];
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
return this.getRecord(address).then(function(record) {
record.updateSessionState(result.session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result.plaintext;
});
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));
}.bind(this));
}.bind(this));
}.bind(this));
@ -36196,6 +36212,7 @@ 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(
@ -36203,7 +36220,7 @@ SessionCipher.prototype = {
).then(function(plaintext) {
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
if (preKeyId !== undefined) {
if (preKeyId !== undefined && preKeyId !== null) {
return this.storage.removePreKey(preKeyId);
}
}.bind(this)).then(function() {
@ -36215,7 +36232,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];
@ -36270,15 +36287,14 @@ SessionCipher.prototype = {
});
},
fillMessageKeys: function(chain, counter) {
if (Object.keys(chain.messageKeys).length >= 1000) {
console.log("Too many message keys for chain");
return Promise.resolve(); // Stalker, much?
}
if (chain.chainKey.counter >= counter) {
return Promise.resolve(); // Already calculated
}
if (counter - chain.chainKey.counter > 2000) {
throw new Error('Over 2000 messages into the future!');
}
if (chain.chainKey.key === undefined) {
throw new Error("Got invalid request to extend chain after it was already closed");
}
@ -36390,6 +36406,20 @@ 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));
}
};
@ -36409,6 +36439,7 @@ 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.1.2",
"version": "1.3.0",
"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": "^0.10.0",
"grunt-contrib-jshint": "^1.1.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.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING
).then(function(trusted) {
if (!trusted) {
throw new Error('Identity key changed');
@ -21,15 +21,20 @@ SessionBuilder.prototype = {
}).then(function() {
return Internal.crypto.createKeyPair();
}).then(function(baseKey) {
var devicePreKey = (device.preKey.publicKey);
var devicePreKey;
if (device.preKey) {
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) {
@ -43,10 +48,10 @@ SessionBuilder.prototype = {
}
record.archiveCurrentState();
record.updateSessionState(session, device.registrationId);
record.updateSessionState(session);
return Promise.all([
this.storage.storeSession(address, record.serialize()),
this.storage.saveIdentity(this.remoteAddress.getName(), session.indexInfo.remoteIdentityKey)
this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey)
]);
}.bind(this));
}.bind(this));
@ -55,7 +60,7 @@ SessionBuilder.prototype = {
processV3: function(record, message) {
var preKeyPair, signedPreKeyPair, session;
return this.storage.isTrustedIdentity(
this.remoteAddress.getName(), message.identityKey.toArrayBuffer()
this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING
).then(function(trusted) {
if (!trusted) {
var e = new Error('Unknown identity key');
@ -102,8 +107,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, message.registrationId);
return this.storage.saveIdentity(this.remoteAddress.getName(), message.identityKey.toArrayBuffer()).then(function() {
record.updateSessionState(new_session);
return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() {
return message.preKeyId;
});
}.bind(this));

View File

@ -76,10 +76,20 @@ SessionCipher.prototype = {
result.set(new Uint8Array(encodedMsg), 1);
result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result;
});
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));
}.bind(this));
}.bind(this));
}.bind(this)).then(function(message) {
@ -89,7 +99,9 @@ SessionCipher.prototype = {
preKeyMsg.registrationId = myRegistrationId;
preKeyMsg.baseKey = util.toArrayBuffer(session.pendingPreKey.baseKey);
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
if (session.pendingPreKey.preKeyId) {
preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
}
preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
preKeyMsg.message = message;
@ -122,6 +134,10 @@ 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));
@ -137,10 +153,25 @@ SessionCipher.prototype = {
var errors = [];
return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
return this.getRecord(address).then(function(record) {
record.updateSessionState(result.session);
return this.storage.storeSession(address, record.serialize()).then(function() {
return result.plaintext;
});
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));
}.bind(this));
}.bind(this));
}.bind(this));
@ -165,6 +196,7 @@ 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(
@ -172,7 +204,7 @@ SessionCipher.prototype = {
).then(function(plaintext) {
record.updateSessionState(session);
return this.storage.storeSession(address, record.serialize()).then(function() {
if (preKeyId !== undefined) {
if (preKeyId !== undefined && preKeyId !== null) {
return this.storage.removePreKey(preKeyId);
}
}.bind(this)).then(function() {
@ -184,7 +216,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];
@ -239,15 +271,14 @@ SessionCipher.prototype = {
});
},
fillMessageKeys: function(chain, counter) {
if (Object.keys(chain.messageKeys).length >= 1000) {
console.log("Too many message keys for chain");
return Promise.resolve(); // Stalker, much?
}
if (chain.chainKey.counter >= counter) {
return Promise.resolve(); // Already calculated
}
if (counter - chain.chainKey.counter > 2000) {
throw new Error('Over 2000 messages into the future!');
}
if (chain.chainKey.key === undefined) {
throw new Error("Got invalid request to extend chain after it was already closed");
}
@ -359,6 +390,20 @@ 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));
}
};
@ -378,4 +423,5 @@ 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,6 +16,7 @@ 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__;
@ -42,7 +43,12 @@ Internal.SessionRecord = function() {
} else if (thing === Object(thing)) {
var obj = {};
for (var key in thing) {
obj[key] = ensureStringed(thing[key]);
try {
obj[key] = ensureStringed(thing[key]);
} catch (ex) {
console.log('Error serializing key', key);
throw ex;
}
}
return obj;
} else if (thing === null) {
@ -61,12 +67,21 @@ Internal.SessionRecord = function() {
version: 'v1',
migrate: function migrateV1(data) {
var sessions = data.sessions;
var key;
if (data.registrationId) {
for (var key in sessions) {
for (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);
}
}
}
}
}
@ -87,7 +102,7 @@ Internal.SessionRecord = function() {
}
var SessionRecord = function() {
this._sessions = {};
this.sessions = {};
this.version = SESSION_RECORD_VERSION;
};
@ -96,8 +111,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;
@ -106,16 +121,17 @@ Internal.SessionRecord = function() {
SessionRecord.prototype = {
serialize: function() {
return jsonThing({
sessions : this._sessions,
sessions : this.sessions,
version : this.version
});
},
haveOpenSession: function() {
return this.getOpenSession() !== undefined;
var openSession = this.getOpenSession();
return (!!openSession && typeof openSession.registrationId === 'number');
},
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;
@ -124,7 +140,7 @@ Internal.SessionRecord = function() {
},
getSessionByRemoteEphemeralKey: function(remoteEphemeralKey) {
this.detectDuplicateOpenSessions();
var sessions = this._sessions;
var sessions = this.sessions;
var searchKey = util.toString(remoteEphemeralKey);
@ -144,7 +160,7 @@ Internal.SessionRecord = function() {
return undefined;
},
getOpenSession: function() {
var sessions = this._sessions;
var sessions = this.sessions;
if (sessions === undefined) {
return undefined;
}
@ -160,7 +176,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) {
@ -171,7 +187,7 @@ Internal.SessionRecord = function() {
}
},
updateSessionState: function(session) {
var sessions = this._sessions;
var sessions = this.sessions;
this.removeOldChains(session);
@ -185,11 +201,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) {
@ -203,38 +219,20 @@ Internal.SessionRecord = function() {
archiveCurrentState: function() {
var open_session = this.getOpenSession();
if (open_session !== undefined) {
this.closeSession(open_session);
console.log('closing session');
open_session.indexInfo.closed = Date.now();
this.updateSessionState(open_session);
}
},
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);
promoteState: function(session) {
console.log('promoting session');
session.indexInfo.closed = -1;
},
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 five.
while (session.oldRatchetList.length > 5) {
// here and remove all but the last ten.
while (session.oldRatchetList.length > OLD_RATCHETS_MAX_LENGTH) {
var index = 0;
var oldest = session.oldRatchetList[0];
for (var i = 0; i < session.oldRatchetList.length; i++) {
@ -250,7 +248,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) {
@ -265,6 +263,10 @@ 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,8 +101,6 @@ 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,6 +1,7 @@
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) {
@ -25,7 +26,7 @@ function testIdentityKeyStore(store, registrationId, identityKey) {
});
describe('saveIdentity', function() {
it('stores identity keys', function(done) {
store.saveIdentity(number, testKey.pubKey).then(function() {
store.saveIdentity(address.toString(), testKey.pubKey).then(function() {
return store.loadIdentityKey(number).then(function(key) {
assertEqualArrayBuffers(key, testKey.pubKey);
});
@ -34,7 +35,7 @@ function testIdentityKeyStore(store, registrationId, identityKey) {
});
describe('isTrustedIdentity', function() {
it('returns true if a key is trusted', function(done) {
store.saveIdentity(number, testKey.pubKey).then(function() {
store.saveIdentity(address.toString(), testKey.pubKey).then(function() {
store.isTrustedIdentity(number, testKey.pubKey).then(function(trusted) {
if (trusted) {
done();
@ -46,7 +47,7 @@ function testIdentityKeyStore(store, registrationId, identityKey) {
});
it('returns false if a key is untrusted', function(done) {
var newIdentity = libsignal.crypto.getRandomBytes(33);
store.saveIdentity(number, testKey.pubKey).then(function() {
store.saveIdentity(address.toString(), testKey.pubKey).then(function() {
store.isTrustedIdentity(number, newIdentity).then(function(trusted) {
if (trusted) {
done(new Error('Wrong value for untrusted key'));

View File

@ -1,96 +1,112 @@
function SignalProtocolStore() {
this.store = {};
function SignalProtocolStore() {
this.store = {};
}
SignalProtocolStore.prototype = {
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];
},
Direction: {
SENDING: 1,
RECEIVING: 2,
},
isTrustedIdentity: function(identifier, identityKey) {
if (identifier === null || identifier === undefined) {
throw new Error("tried to check identity key for undefined/null 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;
}
if (!(identityKey instanceof ArrayBuffer)) {
throw new Error("Expected identityKey to be an ArrayBuffer");
},
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");
}
var trusted = this.get('identityKey' + identifier);
if (!(identityKey instanceof ArrayBuffer)) {
throw new Error("Expected identityKey to be an ArrayBuffer");
}
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");
return Promise.resolve(this.put('identityKey' + identifier, identityKey));
},
},
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");
/* Returns a prekeypair object or undefined */
loadPreKey: function(keyId) {
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) {
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,51 +1,5 @@
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);
@ -142,4 +96,99 @@ 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,4 +365,68 @@ 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,3 +52,50 @@ 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,3 +34453,50 @@ 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
}
};
});
});
}