diff --git a/WASignalGroup/GroupProtocol.js b/WASignalGroup/GroupProtocol.js deleted file mode 100644 index efa722a..0000000 --- a/WASignalGroup/GroupProtocol.js +++ /dev/null @@ -1,1697 +0,0 @@ -/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ -"use strict"; - -var $protobuf = require("protobufjs/minimal"); - -// Common aliases -var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; - -// Exported root namespace -var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); - -$root.groupproto = (function() { - - /** - * Namespace groupproto. - * @exports groupproto - * @namespace - */ - var groupproto = {}; - - groupproto.SenderKeyMessage = (function() { - - /** - * Properties of a SenderKeyMessage. - * @memberof groupproto - * @interface ISenderKeyMessage - * @property {number|null} [id] SenderKeyMessage id - * @property {number|null} [iteration] SenderKeyMessage iteration - * @property {Uint8Array|null} [ciphertext] SenderKeyMessage ciphertext - */ - - /** - * Constructs a new SenderKeyMessage. - * @memberof groupproto - * @classdesc Represents a SenderKeyMessage. - * @implements ISenderKeyMessage - * @constructor - * @param {groupproto.ISenderKeyMessage=} [properties] Properties to set - */ - function SenderKeyMessage(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderKeyMessage id. - * @member {number} id - * @memberof groupproto.SenderKeyMessage - * @instance - */ - SenderKeyMessage.prototype.id = 0; - - /** - * SenderKeyMessage iteration. - * @member {number} iteration - * @memberof groupproto.SenderKeyMessage - * @instance - */ - SenderKeyMessage.prototype.iteration = 0; - - /** - * SenderKeyMessage ciphertext. - * @member {Uint8Array} ciphertext - * @memberof groupproto.SenderKeyMessage - * @instance - */ - SenderKeyMessage.prototype.ciphertext = $util.newBuffer([]); - - /** - * Creates a new SenderKeyMessage instance using the specified properties. - * @function create - * @memberof groupproto.SenderKeyMessage - * @static - * @param {groupproto.ISenderKeyMessage=} [properties] Properties to set - * @returns {groupproto.SenderKeyMessage} SenderKeyMessage instance - */ - SenderKeyMessage.create = function create(properties) { - return new SenderKeyMessage(properties); - }; - - /** - * Encodes the specified SenderKeyMessage message. Does not implicitly {@link groupproto.SenderKeyMessage.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderKeyMessage - * @static - * @param {groupproto.ISenderKeyMessage} message SenderKeyMessage message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyMessage.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.id); - if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) - writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.iteration); - if (message.ciphertext != null && Object.hasOwnProperty.call(message, "ciphertext")) - writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.ciphertext); - return writer; - }; - - /** - * Encodes the specified SenderKeyMessage message, length delimited. Does not implicitly {@link groupproto.SenderKeyMessage.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderKeyMessage - * @static - * @param {groupproto.ISenderKeyMessage} message SenderKeyMessage message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyMessage.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderKeyMessage message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderKeyMessage - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderKeyMessage} SenderKeyMessage - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyMessage.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyMessage(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.id = reader.uint32(); - break; - case 2: - message.iteration = reader.uint32(); - break; - case 3: - message.ciphertext = reader.bytes(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderKeyMessage message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderKeyMessage - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderKeyMessage} SenderKeyMessage - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyMessage.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderKeyMessage message. - * @function verify - * @memberof groupproto.SenderKeyMessage - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderKeyMessage.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.iteration != null && message.hasOwnProperty("iteration")) - if (!$util.isInteger(message.iteration)) - return "iteration: integer expected"; - if (message.ciphertext != null && message.hasOwnProperty("ciphertext")) - if (!(message.ciphertext && typeof message.ciphertext.length === "number" || $util.isString(message.ciphertext))) - return "ciphertext: buffer expected"; - return null; - }; - - /** - * Creates a SenderKeyMessage message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderKeyMessage - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderKeyMessage} SenderKeyMessage - */ - SenderKeyMessage.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderKeyMessage) - return object; - var message = new $root.groupproto.SenderKeyMessage(); - if (object.id != null) - message.id = object.id >>> 0; - if (object.iteration != null) - message.iteration = object.iteration >>> 0; - if (object.ciphertext != null) - if (typeof object.ciphertext === "string") - $util.base64.decode(object.ciphertext, message.ciphertext = $util.newBuffer($util.base64.length(object.ciphertext)), 0); - else if (object.ciphertext.length) - message.ciphertext = object.ciphertext; - return message; - }; - - /** - * Creates a plain object from a SenderKeyMessage message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderKeyMessage - * @static - * @param {groupproto.SenderKeyMessage} message SenderKeyMessage - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderKeyMessage.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = 0; - object.iteration = 0; - if (options.bytes === String) - object.ciphertext = ""; - else { - object.ciphertext = []; - if (options.bytes !== Array) - object.ciphertext = $util.newBuffer(object.ciphertext); - } - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.iteration != null && message.hasOwnProperty("iteration")) - object.iteration = message.iteration; - if (message.ciphertext != null && message.hasOwnProperty("ciphertext")) - object.ciphertext = options.bytes === String ? $util.base64.encode(message.ciphertext, 0, message.ciphertext.length) : options.bytes === Array ? Array.prototype.slice.call(message.ciphertext) : message.ciphertext; - return object; - }; - - /** - * Converts this SenderKeyMessage to JSON. - * @function toJSON - * @memberof groupproto.SenderKeyMessage - * @instance - * @returns {Object.} JSON object - */ - SenderKeyMessage.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderKeyMessage; - })(); - - groupproto.SenderKeyDistributionMessage = (function() { - - /** - * Properties of a SenderKeyDistributionMessage. - * @memberof groupproto - * @interface ISenderKeyDistributionMessage - * @property {number|null} [id] SenderKeyDistributionMessage id - * @property {number|null} [iteration] SenderKeyDistributionMessage iteration - * @property {Uint8Array|null} [chainKey] SenderKeyDistributionMessage chainKey - * @property {Uint8Array|null} [signingKey] SenderKeyDistributionMessage signingKey - */ - - /** - * Constructs a new SenderKeyDistributionMessage. - * @memberof groupproto - * @classdesc Represents a SenderKeyDistributionMessage. - * @implements ISenderKeyDistributionMessage - * @constructor - * @param {groupproto.ISenderKeyDistributionMessage=} [properties] Properties to set - */ - function SenderKeyDistributionMessage(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderKeyDistributionMessage id. - * @member {number} id - * @memberof groupproto.SenderKeyDistributionMessage - * @instance - */ - SenderKeyDistributionMessage.prototype.id = 0; - - /** - * SenderKeyDistributionMessage iteration. - * @member {number} iteration - * @memberof groupproto.SenderKeyDistributionMessage - * @instance - */ - SenderKeyDistributionMessage.prototype.iteration = 0; - - /** - * SenderKeyDistributionMessage chainKey. - * @member {Uint8Array} chainKey - * @memberof groupproto.SenderKeyDistributionMessage - * @instance - */ - SenderKeyDistributionMessage.prototype.chainKey = $util.newBuffer([]); - - /** - * SenderKeyDistributionMessage signingKey. - * @member {Uint8Array} signingKey - * @memberof groupproto.SenderKeyDistributionMessage - * @instance - */ - SenderKeyDistributionMessage.prototype.signingKey = $util.newBuffer([]); - - /** - * Creates a new SenderKeyDistributionMessage instance using the specified properties. - * @function create - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {groupproto.ISenderKeyDistributionMessage=} [properties] Properties to set - * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage instance - */ - SenderKeyDistributionMessage.create = function create(properties) { - return new SenderKeyDistributionMessage(properties); - }; - - /** - * Encodes the specified SenderKeyDistributionMessage message. Does not implicitly {@link groupproto.SenderKeyDistributionMessage.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {groupproto.ISenderKeyDistributionMessage} message SenderKeyDistributionMessage message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyDistributionMessage.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.id != null && Object.hasOwnProperty.call(message, "id")) - writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.id); - if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) - writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.iteration); - if (message.chainKey != null && Object.hasOwnProperty.call(message, "chainKey")) - writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.chainKey); - if (message.signingKey != null && Object.hasOwnProperty.call(message, "signingKey")) - writer.uint32(/* id 4, wireType 2 =*/34).bytes(message.signingKey); - return writer; - }; - - /** - * Encodes the specified SenderKeyDistributionMessage message, length delimited. Does not implicitly {@link groupproto.SenderKeyDistributionMessage.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {groupproto.ISenderKeyDistributionMessage} message SenderKeyDistributionMessage message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyDistributionMessage.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderKeyDistributionMessage message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyDistributionMessage.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyDistributionMessage(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.id = reader.uint32(); - break; - case 2: - message.iteration = reader.uint32(); - break; - case 3: - message.chainKey = reader.bytes(); - break; - case 4: - message.signingKey = reader.bytes(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderKeyDistributionMessage message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyDistributionMessage.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderKeyDistributionMessage message. - * @function verify - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderKeyDistributionMessage.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.id != null && message.hasOwnProperty("id")) - if (!$util.isInteger(message.id)) - return "id: integer expected"; - if (message.iteration != null && message.hasOwnProperty("iteration")) - if (!$util.isInteger(message.iteration)) - return "iteration: integer expected"; - if (message.chainKey != null && message.hasOwnProperty("chainKey")) - if (!(message.chainKey && typeof message.chainKey.length === "number" || $util.isString(message.chainKey))) - return "chainKey: buffer expected"; - if (message.signingKey != null && message.hasOwnProperty("signingKey")) - if (!(message.signingKey && typeof message.signingKey.length === "number" || $util.isString(message.signingKey))) - return "signingKey: buffer expected"; - return null; - }; - - /** - * Creates a SenderKeyDistributionMessage message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderKeyDistributionMessage} SenderKeyDistributionMessage - */ - SenderKeyDistributionMessage.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderKeyDistributionMessage) - return object; - var message = new $root.groupproto.SenderKeyDistributionMessage(); - if (object.id != null) - message.id = object.id >>> 0; - if (object.iteration != null) - message.iteration = object.iteration >>> 0; - if (object.chainKey != null) - if (typeof object.chainKey === "string") - $util.base64.decode(object.chainKey, message.chainKey = $util.newBuffer($util.base64.length(object.chainKey)), 0); - else if (object.chainKey.length) - message.chainKey = object.chainKey; - if (object.signingKey != null) - if (typeof object.signingKey === "string") - $util.base64.decode(object.signingKey, message.signingKey = $util.newBuffer($util.base64.length(object.signingKey)), 0); - else if (object.signingKey.length) - message.signingKey = object.signingKey; - return message; - }; - - /** - * Creates a plain object from a SenderKeyDistributionMessage message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderKeyDistributionMessage - * @static - * @param {groupproto.SenderKeyDistributionMessage} message SenderKeyDistributionMessage - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderKeyDistributionMessage.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.id = 0; - object.iteration = 0; - if (options.bytes === String) - object.chainKey = ""; - else { - object.chainKey = []; - if (options.bytes !== Array) - object.chainKey = $util.newBuffer(object.chainKey); - } - if (options.bytes === String) - object.signingKey = ""; - else { - object.signingKey = []; - if (options.bytes !== Array) - object.signingKey = $util.newBuffer(object.signingKey); - } - } - if (message.id != null && message.hasOwnProperty("id")) - object.id = message.id; - if (message.iteration != null && message.hasOwnProperty("iteration")) - object.iteration = message.iteration; - if (message.chainKey != null && message.hasOwnProperty("chainKey")) - object.chainKey = options.bytes === String ? $util.base64.encode(message.chainKey, 0, message.chainKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.chainKey) : message.chainKey; - if (message.signingKey != null && message.hasOwnProperty("signingKey")) - object.signingKey = options.bytes === String ? $util.base64.encode(message.signingKey, 0, message.signingKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.signingKey) : message.signingKey; - return object; - }; - - /** - * Converts this SenderKeyDistributionMessage to JSON. - * @function toJSON - * @memberof groupproto.SenderKeyDistributionMessage - * @instance - * @returns {Object.} JSON object - */ - SenderKeyDistributionMessage.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderKeyDistributionMessage; - })(); - - groupproto.SenderChainKey = (function() { - - /** - * Properties of a SenderChainKey. - * @memberof groupproto - * @interface ISenderChainKey - * @property {number|null} [iteration] SenderChainKey iteration - * @property {Uint8Array|null} [seed] SenderChainKey seed - */ - - /** - * Constructs a new SenderChainKey. - * @memberof groupproto - * @classdesc Represents a SenderChainKey. - * @implements ISenderChainKey - * @constructor - * @param {groupproto.ISenderChainKey=} [properties] Properties to set - */ - function SenderChainKey(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderChainKey iteration. - * @member {number} iteration - * @memberof groupproto.SenderChainKey - * @instance - */ - SenderChainKey.prototype.iteration = 0; - - /** - * SenderChainKey seed. - * @member {Uint8Array} seed - * @memberof groupproto.SenderChainKey - * @instance - */ - SenderChainKey.prototype.seed = $util.newBuffer([]); - - /** - * Creates a new SenderChainKey instance using the specified properties. - * @function create - * @memberof groupproto.SenderChainKey - * @static - * @param {groupproto.ISenderChainKey=} [properties] Properties to set - * @returns {groupproto.SenderChainKey} SenderChainKey instance - */ - SenderChainKey.create = function create(properties) { - return new SenderChainKey(properties); - }; - - /** - * Encodes the specified SenderChainKey message. Does not implicitly {@link groupproto.SenderChainKey.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderChainKey - * @static - * @param {groupproto.ISenderChainKey} message SenderChainKey message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderChainKey.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) - writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.iteration); - if (message.seed != null && Object.hasOwnProperty.call(message, "seed")) - writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.seed); - return writer; - }; - - /** - * Encodes the specified SenderChainKey message, length delimited. Does not implicitly {@link groupproto.SenderChainKey.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderChainKey - * @static - * @param {groupproto.ISenderChainKey} message SenderChainKey message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderChainKey.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderChainKey message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderChainKey - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderChainKey} SenderChainKey - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderChainKey.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderChainKey(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.iteration = reader.uint32(); - break; - case 2: - message.seed = reader.bytes(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderChainKey message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderChainKey - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderChainKey} SenderChainKey - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderChainKey.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderChainKey message. - * @function verify - * @memberof groupproto.SenderChainKey - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderChainKey.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.iteration != null && message.hasOwnProperty("iteration")) - if (!$util.isInteger(message.iteration)) - return "iteration: integer expected"; - if (message.seed != null && message.hasOwnProperty("seed")) - if (!(message.seed && typeof message.seed.length === "number" || $util.isString(message.seed))) - return "seed: buffer expected"; - return null; - }; - - /** - * Creates a SenderChainKey message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderChainKey - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderChainKey} SenderChainKey - */ - SenderChainKey.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderChainKey) - return object; - var message = new $root.groupproto.SenderChainKey(); - if (object.iteration != null) - message.iteration = object.iteration >>> 0; - if (object.seed != null) - if (typeof object.seed === "string") - $util.base64.decode(object.seed, message.seed = $util.newBuffer($util.base64.length(object.seed)), 0); - else if (object.seed.length) - message.seed = object.seed; - return message; - }; - - /** - * Creates a plain object from a SenderChainKey message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderChainKey - * @static - * @param {groupproto.SenderChainKey} message SenderChainKey - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderChainKey.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.iteration = 0; - if (options.bytes === String) - object.seed = ""; - else { - object.seed = []; - if (options.bytes !== Array) - object.seed = $util.newBuffer(object.seed); - } - } - if (message.iteration != null && message.hasOwnProperty("iteration")) - object.iteration = message.iteration; - if (message.seed != null && message.hasOwnProperty("seed")) - object.seed = options.bytes === String ? $util.base64.encode(message.seed, 0, message.seed.length) : options.bytes === Array ? Array.prototype.slice.call(message.seed) : message.seed; - return object; - }; - - /** - * Converts this SenderChainKey to JSON. - * @function toJSON - * @memberof groupproto.SenderChainKey - * @instance - * @returns {Object.} JSON object - */ - SenderChainKey.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderChainKey; - })(); - - groupproto.SenderMessageKey = (function() { - - /** - * Properties of a SenderMessageKey. - * @memberof groupproto - * @interface ISenderMessageKey - * @property {number|null} [iteration] SenderMessageKey iteration - * @property {Uint8Array|null} [seed] SenderMessageKey seed - */ - - /** - * Constructs a new SenderMessageKey. - * @memberof groupproto - * @classdesc Represents a SenderMessageKey. - * @implements ISenderMessageKey - * @constructor - * @param {groupproto.ISenderMessageKey=} [properties] Properties to set - */ - function SenderMessageKey(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderMessageKey iteration. - * @member {number} iteration - * @memberof groupproto.SenderMessageKey - * @instance - */ - SenderMessageKey.prototype.iteration = 0; - - /** - * SenderMessageKey seed. - * @member {Uint8Array} seed - * @memberof groupproto.SenderMessageKey - * @instance - */ - SenderMessageKey.prototype.seed = $util.newBuffer([]); - - /** - * Creates a new SenderMessageKey instance using the specified properties. - * @function create - * @memberof groupproto.SenderMessageKey - * @static - * @param {groupproto.ISenderMessageKey=} [properties] Properties to set - * @returns {groupproto.SenderMessageKey} SenderMessageKey instance - */ - SenderMessageKey.create = function create(properties) { - return new SenderMessageKey(properties); - }; - - /** - * Encodes the specified SenderMessageKey message. Does not implicitly {@link groupproto.SenderMessageKey.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderMessageKey - * @static - * @param {groupproto.ISenderMessageKey} message SenderMessageKey message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderMessageKey.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.iteration != null && Object.hasOwnProperty.call(message, "iteration")) - writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.iteration); - if (message.seed != null && Object.hasOwnProperty.call(message, "seed")) - writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.seed); - return writer; - }; - - /** - * Encodes the specified SenderMessageKey message, length delimited. Does not implicitly {@link groupproto.SenderMessageKey.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderMessageKey - * @static - * @param {groupproto.ISenderMessageKey} message SenderMessageKey message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderMessageKey.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderMessageKey message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderMessageKey - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderMessageKey} SenderMessageKey - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderMessageKey.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderMessageKey(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.iteration = reader.uint32(); - break; - case 2: - message.seed = reader.bytes(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderMessageKey message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderMessageKey - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderMessageKey} SenderMessageKey - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderMessageKey.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderMessageKey message. - * @function verify - * @memberof groupproto.SenderMessageKey - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderMessageKey.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.iteration != null && message.hasOwnProperty("iteration")) - if (!$util.isInteger(message.iteration)) - return "iteration: integer expected"; - if (message.seed != null && message.hasOwnProperty("seed")) - if (!(message.seed && typeof message.seed.length === "number" || $util.isString(message.seed))) - return "seed: buffer expected"; - return null; - }; - - /** - * Creates a SenderMessageKey message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderMessageKey - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderMessageKey} SenderMessageKey - */ - SenderMessageKey.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderMessageKey) - return object; - var message = new $root.groupproto.SenderMessageKey(); - if (object.iteration != null) - message.iteration = object.iteration >>> 0; - if (object.seed != null) - if (typeof object.seed === "string") - $util.base64.decode(object.seed, message.seed = $util.newBuffer($util.base64.length(object.seed)), 0); - else if (object.seed.length) - message.seed = object.seed; - return message; - }; - - /** - * Creates a plain object from a SenderMessageKey message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderMessageKey - * @static - * @param {groupproto.SenderMessageKey} message SenderMessageKey - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderMessageKey.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - object.iteration = 0; - if (options.bytes === String) - object.seed = ""; - else { - object.seed = []; - if (options.bytes !== Array) - object.seed = $util.newBuffer(object.seed); - } - } - if (message.iteration != null && message.hasOwnProperty("iteration")) - object.iteration = message.iteration; - if (message.seed != null && message.hasOwnProperty("seed")) - object.seed = options.bytes === String ? $util.base64.encode(message.seed, 0, message.seed.length) : options.bytes === Array ? Array.prototype.slice.call(message.seed) : message.seed; - return object; - }; - - /** - * Converts this SenderMessageKey to JSON. - * @function toJSON - * @memberof groupproto.SenderMessageKey - * @instance - * @returns {Object.} JSON object - */ - SenderMessageKey.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderMessageKey; - })(); - - groupproto.SenderSigningKey = (function() { - - /** - * Properties of a SenderSigningKey. - * @memberof groupproto - * @interface ISenderSigningKey - * @property {Uint8Array|null} ["public"] SenderSigningKey public - * @property {Uint8Array|null} ["private"] SenderSigningKey private - */ - - /** - * Constructs a new SenderSigningKey. - * @memberof groupproto - * @classdesc Represents a SenderSigningKey. - * @implements ISenderSigningKey - * @constructor - * @param {groupproto.ISenderSigningKey=} [properties] Properties to set - */ - function SenderSigningKey(properties) { - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderSigningKey public. - * @member {Uint8Array} public - * @memberof groupproto.SenderSigningKey - * @instance - */ - SenderSigningKey.prototype["public"] = $util.newBuffer([]); - - /** - * SenderSigningKey private. - * @member {Uint8Array} private - * @memberof groupproto.SenderSigningKey - * @instance - */ - SenderSigningKey.prototype["private"] = $util.newBuffer([]); - - /** - * Creates a new SenderSigningKey instance using the specified properties. - * @function create - * @memberof groupproto.SenderSigningKey - * @static - * @param {groupproto.ISenderSigningKey=} [properties] Properties to set - * @returns {groupproto.SenderSigningKey} SenderSigningKey instance - */ - SenderSigningKey.create = function create(properties) { - return new SenderSigningKey(properties); - }; - - /** - * Encodes the specified SenderSigningKey message. Does not implicitly {@link groupproto.SenderSigningKey.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderSigningKey - * @static - * @param {groupproto.ISenderSigningKey} message SenderSigningKey message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderSigningKey.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message["public"] != null && Object.hasOwnProperty.call(message, "public")) - writer.uint32(/* id 1, wireType 2 =*/10).bytes(message["public"]); - if (message["private"] != null && Object.hasOwnProperty.call(message, "private")) - writer.uint32(/* id 2, wireType 2 =*/18).bytes(message["private"]); - return writer; - }; - - /** - * Encodes the specified SenderSigningKey message, length delimited. Does not implicitly {@link groupproto.SenderSigningKey.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderSigningKey - * @static - * @param {groupproto.ISenderSigningKey} message SenderSigningKey message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderSigningKey.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderSigningKey message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderSigningKey - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderSigningKey} SenderSigningKey - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderSigningKey.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderSigningKey(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message["public"] = reader.bytes(); - break; - case 2: - message["private"] = reader.bytes(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderSigningKey message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderSigningKey - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderSigningKey} SenderSigningKey - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderSigningKey.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderSigningKey message. - * @function verify - * @memberof groupproto.SenderSigningKey - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderSigningKey.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message["public"] != null && message.hasOwnProperty("public")) - if (!(message["public"] && typeof message["public"].length === "number" || $util.isString(message["public"]))) - return "public: buffer expected"; - if (message["private"] != null && message.hasOwnProperty("private")) - if (!(message["private"] && typeof message["private"].length === "number" || $util.isString(message["private"]))) - return "private: buffer expected"; - return null; - }; - - /** - * Creates a SenderSigningKey message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderSigningKey - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderSigningKey} SenderSigningKey - */ - SenderSigningKey.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderSigningKey) - return object; - var message = new $root.groupproto.SenderSigningKey(); - if (object["public"] != null) - if (typeof object["public"] === "string") - $util.base64.decode(object["public"], message["public"] = $util.newBuffer($util.base64.length(object["public"])), 0); - else if (object["public"].length) - message["public"] = object["public"]; - if (object["private"] != null) - if (typeof object["private"] === "string") - $util.base64.decode(object["private"], message["private"] = $util.newBuffer($util.base64.length(object["private"])), 0); - else if (object["private"].length) - message["private"] = object["private"]; - return message; - }; - - /** - * Creates a plain object from a SenderSigningKey message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderSigningKey - * @static - * @param {groupproto.SenderSigningKey} message SenderSigningKey - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderSigningKey.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.defaults) { - if (options.bytes === String) - object["public"] = ""; - else { - object["public"] = []; - if (options.bytes !== Array) - object["public"] = $util.newBuffer(object["public"]); - } - if (options.bytes === String) - object["private"] = ""; - else { - object["private"] = []; - if (options.bytes !== Array) - object["private"] = $util.newBuffer(object["private"]); - } - } - if (message["public"] != null && message.hasOwnProperty("public")) - object["public"] = options.bytes === String ? $util.base64.encode(message["public"], 0, message["public"].length) : options.bytes === Array ? Array.prototype.slice.call(message["public"]) : message["public"]; - if (message["private"] != null && message.hasOwnProperty("private")) - object["private"] = options.bytes === String ? $util.base64.encode(message["private"], 0, message["private"].length) : options.bytes === Array ? Array.prototype.slice.call(message["private"]) : message["private"]; - return object; - }; - - /** - * Converts this SenderSigningKey to JSON. - * @function toJSON - * @memberof groupproto.SenderSigningKey - * @instance - * @returns {Object.} JSON object - */ - SenderSigningKey.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderSigningKey; - })(); - - groupproto.SenderKeyStateStructure = (function() { - - /** - * Properties of a SenderKeyStateStructure. - * @memberof groupproto - * @interface ISenderKeyStateStructure - * @property {number|null} [senderKeyId] SenderKeyStateStructure senderKeyId - * @property {groupproto.ISenderChainKey|null} [senderChainKey] SenderKeyStateStructure senderChainKey - * @property {groupproto.ISenderSigningKey|null} [senderSigningKey] SenderKeyStateStructure senderSigningKey - * @property {Array.|null} [senderMessageKeys] SenderKeyStateStructure senderMessageKeys - */ - - /** - * Constructs a new SenderKeyStateStructure. - * @memberof groupproto - * @classdesc Represents a SenderKeyStateStructure. - * @implements ISenderKeyStateStructure - * @constructor - * @param {groupproto.ISenderKeyStateStructure=} [properties] Properties to set - */ - function SenderKeyStateStructure(properties) { - this.senderMessageKeys = []; - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderKeyStateStructure senderKeyId. - * @member {number} senderKeyId - * @memberof groupproto.SenderKeyStateStructure - * @instance - */ - SenderKeyStateStructure.prototype.senderKeyId = 0; - - /** - * SenderKeyStateStructure senderChainKey. - * @member {groupproto.ISenderChainKey|null|undefined} senderChainKey - * @memberof groupproto.SenderKeyStateStructure - * @instance - */ - SenderKeyStateStructure.prototype.senderChainKey = null; - - /** - * SenderKeyStateStructure senderSigningKey. - * @member {groupproto.ISenderSigningKey|null|undefined} senderSigningKey - * @memberof groupproto.SenderKeyStateStructure - * @instance - */ - SenderKeyStateStructure.prototype.senderSigningKey = null; - - /** - * SenderKeyStateStructure senderMessageKeys. - * @member {Array.} senderMessageKeys - * @memberof groupproto.SenderKeyStateStructure - * @instance - */ - SenderKeyStateStructure.prototype.senderMessageKeys = $util.emptyArray; - - /** - * Creates a new SenderKeyStateStructure instance using the specified properties. - * @function create - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {groupproto.ISenderKeyStateStructure=} [properties] Properties to set - * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure instance - */ - SenderKeyStateStructure.create = function create(properties) { - return new SenderKeyStateStructure(properties); - }; - - /** - * Encodes the specified SenderKeyStateStructure message. Does not implicitly {@link groupproto.SenderKeyStateStructure.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {groupproto.ISenderKeyStateStructure} message SenderKeyStateStructure message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyStateStructure.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.senderKeyId != null && Object.hasOwnProperty.call(message, "senderKeyId")) - writer.uint32(/* id 1, wireType 0 =*/8).uint32(message.senderKeyId); - if (message.senderChainKey != null && Object.hasOwnProperty.call(message, "senderChainKey")) - $root.groupproto.SenderChainKey.encode(message.senderChainKey, writer.uint32(/* id 2, wireType 2 =*/18).fork()).ldelim(); - if (message.senderSigningKey != null && Object.hasOwnProperty.call(message, "senderSigningKey")) - $root.groupproto.SenderSigningKey.encode(message.senderSigningKey, writer.uint32(/* id 3, wireType 2 =*/26).fork()).ldelim(); - if (message.senderMessageKeys != null && message.senderMessageKeys.length) - for (var i = 0; i < message.senderMessageKeys.length; ++i) - $root.groupproto.SenderMessageKey.encode(message.senderMessageKeys[i], writer.uint32(/* id 4, wireType 2 =*/34).fork()).ldelim(); - return writer; - }; - - /** - * Encodes the specified SenderKeyStateStructure message, length delimited. Does not implicitly {@link groupproto.SenderKeyStateStructure.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {groupproto.ISenderKeyStateStructure} message SenderKeyStateStructure message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyStateStructure.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderKeyStateStructure message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyStateStructure.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyStateStructure(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.senderKeyId = reader.uint32(); - break; - case 2: - message.senderChainKey = $root.groupproto.SenderChainKey.decode(reader, reader.uint32()); - break; - case 3: - message.senderSigningKey = $root.groupproto.SenderSigningKey.decode(reader, reader.uint32()); - break; - case 4: - if (!(message.senderMessageKeys && message.senderMessageKeys.length)) - message.senderMessageKeys = []; - message.senderMessageKeys.push($root.groupproto.SenderMessageKey.decode(reader, reader.uint32())); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderKeyStateStructure message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyStateStructure.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderKeyStateStructure message. - * @function verify - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderKeyStateStructure.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.senderKeyId != null && message.hasOwnProperty("senderKeyId")) - if (!$util.isInteger(message.senderKeyId)) - return "senderKeyId: integer expected"; - if (message.senderChainKey != null && message.hasOwnProperty("senderChainKey")) { - var error = $root.groupproto.SenderChainKey.verify(message.senderChainKey); - if (error) - return "senderChainKey." + error; - } - if (message.senderSigningKey != null && message.hasOwnProperty("senderSigningKey")) { - var error = $root.groupproto.SenderSigningKey.verify(message.senderSigningKey); - if (error) - return "senderSigningKey." + error; - } - if (message.senderMessageKeys != null && message.hasOwnProperty("senderMessageKeys")) { - if (!Array.isArray(message.senderMessageKeys)) - return "senderMessageKeys: array expected"; - for (var i = 0; i < message.senderMessageKeys.length; ++i) { - var error = $root.groupproto.SenderMessageKey.verify(message.senderMessageKeys[i]); - if (error) - return "senderMessageKeys." + error; - } - } - return null; - }; - - /** - * Creates a SenderKeyStateStructure message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderKeyStateStructure} SenderKeyStateStructure - */ - SenderKeyStateStructure.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderKeyStateStructure) - return object; - var message = new $root.groupproto.SenderKeyStateStructure(); - if (object.senderKeyId != null) - message.senderKeyId = object.senderKeyId >>> 0; - if (object.senderChainKey != null) { - if (typeof object.senderChainKey !== "object") - throw TypeError(".groupproto.SenderKeyStateStructure.senderChainKey: object expected"); - message.senderChainKey = $root.groupproto.SenderChainKey.fromObject(object.senderChainKey); - } - if (object.senderSigningKey != null) { - if (typeof object.senderSigningKey !== "object") - throw TypeError(".groupproto.SenderKeyStateStructure.senderSigningKey: object expected"); - message.senderSigningKey = $root.groupproto.SenderSigningKey.fromObject(object.senderSigningKey); - } - if (object.senderMessageKeys) { - if (!Array.isArray(object.senderMessageKeys)) - throw TypeError(".groupproto.SenderKeyStateStructure.senderMessageKeys: array expected"); - message.senderMessageKeys = []; - for (var i = 0; i < object.senderMessageKeys.length; ++i) { - if (typeof object.senderMessageKeys[i] !== "object") - throw TypeError(".groupproto.SenderKeyStateStructure.senderMessageKeys: object expected"); - message.senderMessageKeys[i] = $root.groupproto.SenderMessageKey.fromObject(object.senderMessageKeys[i]); - } - } - return message; - }; - - /** - * Creates a plain object from a SenderKeyStateStructure message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderKeyStateStructure - * @static - * @param {groupproto.SenderKeyStateStructure} message SenderKeyStateStructure - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderKeyStateStructure.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.arrays || options.defaults) - object.senderMessageKeys = []; - if (options.defaults) { - object.senderKeyId = 0; - object.senderChainKey = null; - object.senderSigningKey = null; - } - if (message.senderKeyId != null && message.hasOwnProperty("senderKeyId")) - object.senderKeyId = message.senderKeyId; - if (message.senderChainKey != null && message.hasOwnProperty("senderChainKey")) - object.senderChainKey = $root.groupproto.SenderChainKey.toObject(message.senderChainKey, options); - if (message.senderSigningKey != null && message.hasOwnProperty("senderSigningKey")) - object.senderSigningKey = $root.groupproto.SenderSigningKey.toObject(message.senderSigningKey, options); - if (message.senderMessageKeys && message.senderMessageKeys.length) { - object.senderMessageKeys = []; - for (var j = 0; j < message.senderMessageKeys.length; ++j) - object.senderMessageKeys[j] = $root.groupproto.SenderMessageKey.toObject(message.senderMessageKeys[j], options); - } - return object; - }; - - /** - * Converts this SenderKeyStateStructure to JSON. - * @function toJSON - * @memberof groupproto.SenderKeyStateStructure - * @instance - * @returns {Object.} JSON object - */ - SenderKeyStateStructure.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderKeyStateStructure; - })(); - - groupproto.SenderKeyRecordStructure = (function() { - - /** - * Properties of a SenderKeyRecordStructure. - * @memberof groupproto - * @interface ISenderKeyRecordStructure - * @property {Array.|null} [senderKeyStates] SenderKeyRecordStructure senderKeyStates - */ - - /** - * Constructs a new SenderKeyRecordStructure. - * @memberof groupproto - * @classdesc Represents a SenderKeyRecordStructure. - * @implements ISenderKeyRecordStructure - * @constructor - * @param {groupproto.ISenderKeyRecordStructure=} [properties] Properties to set - */ - function SenderKeyRecordStructure(properties) { - this.senderKeyStates = []; - if (properties) - for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) - if (properties[keys[i]] != null) - this[keys[i]] = properties[keys[i]]; - } - - /** - * SenderKeyRecordStructure senderKeyStates. - * @member {Array.} senderKeyStates - * @memberof groupproto.SenderKeyRecordStructure - * @instance - */ - SenderKeyRecordStructure.prototype.senderKeyStates = $util.emptyArray; - - /** - * Creates a new SenderKeyRecordStructure instance using the specified properties. - * @function create - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {groupproto.ISenderKeyRecordStructure=} [properties] Properties to set - * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure instance - */ - SenderKeyRecordStructure.create = function create(properties) { - return new SenderKeyRecordStructure(properties); - }; - - /** - * Encodes the specified SenderKeyRecordStructure message. Does not implicitly {@link groupproto.SenderKeyRecordStructure.verify|verify} messages. - * @function encode - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {groupproto.ISenderKeyRecordStructure} message SenderKeyRecordStructure message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyRecordStructure.encode = function encode(message, writer) { - if (!writer) - writer = $Writer.create(); - if (message.senderKeyStates != null && message.senderKeyStates.length) - for (var i = 0; i < message.senderKeyStates.length; ++i) - $root.groupproto.SenderKeyStateStructure.encode(message.senderKeyStates[i], writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); - return writer; - }; - - /** - * Encodes the specified SenderKeyRecordStructure message, length delimited. Does not implicitly {@link groupproto.SenderKeyRecordStructure.verify|verify} messages. - * @function encodeDelimited - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {groupproto.ISenderKeyRecordStructure} message SenderKeyRecordStructure message or plain object to encode - * @param {$protobuf.Writer} [writer] Writer to encode to - * @returns {$protobuf.Writer} Writer - */ - SenderKeyRecordStructure.encodeDelimited = function encodeDelimited(message, writer) { - return this.encode(message, writer).ldelim(); - }; - - /** - * Decodes a SenderKeyRecordStructure message from the specified reader or buffer. - * @function decode - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @param {number} [length] Message length if known beforehand - * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyRecordStructure.decode = function decode(reader, length) { - if (!(reader instanceof $Reader)) - reader = $Reader.create(reader); - var end = length === undefined ? reader.len : reader.pos + length, message = new $root.groupproto.SenderKeyRecordStructure(); - while (reader.pos < end) { - var tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (!(message.senderKeyStates && message.senderKeyStates.length)) - message.senderKeyStates = []; - message.senderKeyStates.push($root.groupproto.SenderKeyStateStructure.decode(reader, reader.uint32())); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }; - - /** - * Decodes a SenderKeyRecordStructure message from the specified reader or buffer, length delimited. - * @function decodeDelimited - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from - * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure - * @throws {Error} If the payload is not a reader or valid buffer - * @throws {$protobuf.util.ProtocolError} If required fields are missing - */ - SenderKeyRecordStructure.decodeDelimited = function decodeDelimited(reader) { - if (!(reader instanceof $Reader)) - reader = new $Reader(reader); - return this.decode(reader, reader.uint32()); - }; - - /** - * Verifies a SenderKeyRecordStructure message. - * @function verify - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {Object.} message Plain object to verify - * @returns {string|null} `null` if valid, otherwise the reason why it is not - */ - SenderKeyRecordStructure.verify = function verify(message) { - if (typeof message !== "object" || message === null) - return "object expected"; - if (message.senderKeyStates != null && message.hasOwnProperty("senderKeyStates")) { - if (!Array.isArray(message.senderKeyStates)) - return "senderKeyStates: array expected"; - for (var i = 0; i < message.senderKeyStates.length; ++i) { - var error = $root.groupproto.SenderKeyStateStructure.verify(message.senderKeyStates[i]); - if (error) - return "senderKeyStates." + error; - } - } - return null; - }; - - /** - * Creates a SenderKeyRecordStructure message from a plain object. Also converts values to their respective internal types. - * @function fromObject - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {Object.} object Plain object - * @returns {groupproto.SenderKeyRecordStructure} SenderKeyRecordStructure - */ - SenderKeyRecordStructure.fromObject = function fromObject(object) { - if (object instanceof $root.groupproto.SenderKeyRecordStructure) - return object; - var message = new $root.groupproto.SenderKeyRecordStructure(); - if (object.senderKeyStates) { - if (!Array.isArray(object.senderKeyStates)) - throw TypeError(".groupproto.SenderKeyRecordStructure.senderKeyStates: array expected"); - message.senderKeyStates = []; - for (var i = 0; i < object.senderKeyStates.length; ++i) { - if (typeof object.senderKeyStates[i] !== "object") - throw TypeError(".groupproto.SenderKeyRecordStructure.senderKeyStates: object expected"); - message.senderKeyStates[i] = $root.groupproto.SenderKeyStateStructure.fromObject(object.senderKeyStates[i]); - } - } - return message; - }; - - /** - * Creates a plain object from a SenderKeyRecordStructure message. Also converts values to other types if specified. - * @function toObject - * @memberof groupproto.SenderKeyRecordStructure - * @static - * @param {groupproto.SenderKeyRecordStructure} message SenderKeyRecordStructure - * @param {$protobuf.IConversionOptions} [options] Conversion options - * @returns {Object.} Plain object - */ - SenderKeyRecordStructure.toObject = function toObject(message, options) { - if (!options) - options = {}; - var object = {}; - if (options.arrays || options.defaults) - object.senderKeyStates = []; - if (message.senderKeyStates && message.senderKeyStates.length) { - object.senderKeyStates = []; - for (var j = 0; j < message.senderKeyStates.length; ++j) - object.senderKeyStates[j] = $root.groupproto.SenderKeyStateStructure.toObject(message.senderKeyStates[j], options); - } - return object; - }; - - /** - * Converts this SenderKeyRecordStructure to JSON. - * @function toJSON - * @memberof groupproto.SenderKeyRecordStructure - * @instance - * @returns {Object.} JSON object - */ - SenderKeyRecordStructure.prototype.toJSON = function toJSON() { - return this.constructor.toObject(this, $protobuf.util.toJSONOptions); - }; - - return SenderKeyRecordStructure; - })(); - - return groupproto; -})(); - -module.exports = $root; diff --git a/WASignalGroup/ciphertext_message.js b/WASignalGroup/ciphertext_message.js deleted file mode 100644 index f9bb771..0000000 --- a/WASignalGroup/ciphertext_message.js +++ /dev/null @@ -1,16 +0,0 @@ -class CiphertextMessage { - UNSUPPORTED_VERSION = 1; - - CURRENT_VERSION = 3; - - WHISPER_TYPE = 2; - - PREKEY_TYPE = 3; - - SENDERKEY_TYPE = 4; - - SENDERKEY_DISTRIBUTION_TYPE = 5; - - ENCRYPTED_MESSAGE_OVERHEAD = 53; -} -module.exports = CiphertextMessage; \ No newline at end of file diff --git a/WASignalGroup/generate-proto.sh b/WASignalGroup/generate-proto.sh deleted file mode 100644 index 051876d..0000000 --- a/WASignalGroup/generate-proto.sh +++ /dev/null @@ -1 +0,0 @@ -yarn pbjs -t static-module -w commonjs -o ./WASignalGroup/GroupProtocol.js ./WASignalGroup/group.proto \ No newline at end of file diff --git a/WASignalGroup/group.proto b/WASignalGroup/group.proto deleted file mode 100644 index e6f549b..0000000 --- a/WASignalGroup/group.proto +++ /dev/null @@ -1,42 +0,0 @@ -package groupproto; - -message SenderKeyMessage { - optional uint32 id = 1; - optional uint32 iteration = 2; - optional bytes ciphertext = 3; - } - - message SenderKeyDistributionMessage { - optional uint32 id = 1; - optional uint32 iteration = 2; - optional bytes chainKey = 3; - optional bytes signingKey = 4; - } - - message SenderChainKey { - optional uint32 iteration = 1; - optional bytes seed = 2; -} - -message SenderMessageKey { - optional uint32 iteration = 1; - optional bytes seed = 2; -} - -message SenderSigningKey { - optional bytes public = 1; - optional bytes private = 2; -} - -message SenderKeyStateStructure { - - - optional uint32 senderKeyId = 1; - optional SenderChainKey senderChainKey = 2; - optional SenderSigningKey senderSigningKey = 3; - repeated SenderMessageKey senderMessageKeys = 4; -} - -message SenderKeyRecordStructure { - repeated SenderKeyStateStructure senderKeyStates = 1; -} \ No newline at end of file diff --git a/WASignalGroup/group_cipher.js b/WASignalGroup/group_cipher.js deleted file mode 100644 index 2733fa8..0000000 --- a/WASignalGroup/group_cipher.js +++ /dev/null @@ -1,120 +0,0 @@ -const queue_job = require('./queue_job'); -const SenderKeyMessage = require('./sender_key_message'); -const crypto = require('libsignal/src/crypto'); - -class GroupCipher { - constructor(senderKeyStore, senderKeyName) { - this.senderKeyStore = senderKeyStore; - this.senderKeyName = senderKeyName; - } - - queueJob(awaitable) { - return queue_job(this.senderKeyName.toString(), awaitable) - } - - async encrypt(paddedPlaintext) { - return await this.queueJob(async () => { - const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName); - if (!record) { - throw new Error("No SenderKeyRecord found for encryption") - } - const senderKeyState = record.getSenderKeyState(); - if (!senderKeyState) { - throw new Error("No session to encrypt message"); - } - const iteration = senderKeyState.getSenderChainKey().getIteration() - const senderKey = this.getSenderKey(senderKeyState, iteration === 0 ? 0 : iteration + 1) - - const ciphertext = await this.getCipherText( - senderKey.getIv(), - senderKey.getCipherKey(), - paddedPlaintext - ); - - const senderKeyMessage = new SenderKeyMessage( - senderKeyState.getKeyId(), - senderKey.getIteration(), - ciphertext, - senderKeyState.getSigningKeyPrivate() - ); - await this.senderKeyStore.storeSenderKey(this.senderKeyName, record); - return senderKeyMessage.serialize() - }) - } - - async decrypt(senderKeyMessageBytes) { - return await this.queueJob(async () => { - const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName); - if (!record) { - throw new Error("No SenderKeyRecord found for decryption") - } - const senderKeyMessage = new SenderKeyMessage(null, null, null, null, senderKeyMessageBytes); - const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); - if (!senderKeyState) { - throw new Error("No session found to decrypt message") - } - - senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); - const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration()); - // senderKeyState.senderKeyStateStructure.senderSigningKey.private = - - const plaintext = await this.getPlainText( - senderKey.getIv(), - senderKey.getCipherKey(), - senderKeyMessage.getCipherText() - ); - - await this.senderKeyStore.storeSenderKey(this.senderKeyName, record); - - return plaintext; - }) - } - - getSenderKey(senderKeyState, iteration) { - let senderChainKey = senderKeyState.getSenderChainKey(); - if (senderChainKey.getIteration() > iteration) { - if (senderKeyState.hasSenderMessageKey(iteration)) { - return senderKeyState.removeSenderMessageKey(iteration); - } - throw new Error( - `Received message with old counter: ${senderChainKey.getIteration()}, ${iteration}` - ); - } - - if (iteration - senderChainKey.getIteration() > 2000) { - throw new Error('Over 2000 messages into the future!'); - } - - while (senderChainKey.getIteration() < iteration) { - senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()); - senderChainKey = senderChainKey.getNext(); - } - - senderKeyState.setSenderChainKey(senderChainKey.getNext()); - return senderChainKey.getSenderMessageKey(); - } - - getPlainText(iv, key, ciphertext) { - try { - const plaintext = crypto.decrypt(key, ciphertext, iv); - return plaintext; - } catch (e) { - //console.log(e.stack); - throw new Error('InvalidMessageException'); - } - } - - getCipherText(iv, key, plaintext) { - try { - iv = typeof iv === 'string' ? Buffer.from(iv, 'base64') : iv; - key = typeof key === 'string' ? Buffer.from(key, 'base64') : key; - const crypted = crypto.encrypt(key, Buffer.from(plaintext), iv); - return crypted; - } catch (e) { - //console.log(e.stack); - throw new Error('InvalidMessageException'); - } - } -} - -module.exports = GroupCipher; \ No newline at end of file diff --git a/WASignalGroup/group_session_builder.js b/WASignalGroup/group_session_builder.js deleted file mode 100644 index 24c84a2..0000000 --- a/WASignalGroup/group_session_builder.js +++ /dev/null @@ -1,46 +0,0 @@ -//const utils = require('../../common/utils'); -const SenderKeyDistributionMessage = require('./sender_key_distribution_message'); - -const keyhelper = require("./keyhelper"); -class GroupSessionBuilder { - constructor(senderKeyStore) { - this.senderKeyStore = senderKeyStore; - } - - async process(senderKeyName, senderKeyDistributionMessage) { - //console.log('GroupSessionBuilder process', senderKeyName, senderKeyDistributionMessage); - const senderKeyRecord = await this.senderKeyStore.loadSenderKey(senderKeyName); - senderKeyRecord.addSenderKeyState( - senderKeyDistributionMessage.getId(), - senderKeyDistributionMessage.getIteration(), - senderKeyDistributionMessage.getChainKey(), - senderKeyDistributionMessage.getSignatureKey() - ); - await this.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); - } - - // [{"senderKeyId":1742199468,"senderChainKey":{"iteration":0,"seed":"yxMY9VFQcXEP34olRAcGCtsgx1XoKsHfDIh+1ea4HAQ="},"senderSigningKey":{"public":""}}] - async create(senderKeyName) { - const senderKeyRecord = await this.senderKeyStore.loadSenderKey(senderKeyName); - //console.log('GroupSessionBuilder create session', senderKeyName, senderKeyRecord); - - if (senderKeyRecord.isEmpty()) { - const keyId = keyhelper.generateSenderKeyId(); - const senderKey = keyhelper.generateSenderKey(); - const signingKey = keyhelper.generateSenderSigningKey(); - - senderKeyRecord.setSenderKeyState(keyId, 0, senderKey, signingKey); - await this.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); - } - - const state = senderKeyRecord.getSenderKeyState(); - - return new SenderKeyDistributionMessage( - state.getKeyId(), - state.getSenderChainKey().getIteration(), - state.getSenderChainKey().getSeed(), - state.getSigningKeyPublic() - ); - } -} -module.exports = GroupSessionBuilder; \ No newline at end of file diff --git a/WASignalGroup/index.js b/WASignalGroup/index.js deleted file mode 100644 index 69b935d..0000000 --- a/WASignalGroup/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports.GroupSessionBuilder = require('./group_session_builder') -module.exports.SenderKeyDistributionMessage = require('./sender_key_distribution_message') -module.exports.SenderKeyRecord = require('./sender_key_record') -module.exports.SenderKeyName = require('./sender_key_name') -module.exports.GroupCipher = require('./group_cipher') \ No newline at end of file diff --git a/WASignalGroup/keyhelper.js b/WASignalGroup/keyhelper.js deleted file mode 100644 index a244581..0000000 --- a/WASignalGroup/keyhelper.js +++ /dev/null @@ -1,21 +0,0 @@ -const curve = require('libsignal/src/curve'); -const nodeCrypto = require('crypto'); - -exports.generateSenderKey = function() { - return nodeCrypto.randomBytes(32); -} - -exports.generateSenderKeyId = function() { - return nodeCrypto.randomInt(2147483647); -} - -exports.generateSenderSigningKey = function(key) { - if (!key) { - key = curve.generateKeyPair(); - } - - return { - public: key.pubKey, - private: key.privKey, - }; -} diff --git a/WASignalGroup/protobufs.js b/WASignalGroup/protobufs.js deleted file mode 100644 index 76daa5c..0000000 --- a/WASignalGroup/protobufs.js +++ /dev/null @@ -1,3 +0,0 @@ -const { groupproto } = require('./GroupProtocol') - -module.exports = groupproto \ No newline at end of file diff --git a/WASignalGroup/queue_job.js b/WASignalGroup/queue_job.js deleted file mode 100644 index df0c324..0000000 --- a/WASignalGroup/queue_job.js +++ /dev/null @@ -1,69 +0,0 @@ -// vim: ts=4:sw=4:expandtab - -/* - * jobQueue manages multiple queues indexed by device to serialize - * session io ops on the database. - */ -'use strict'; - - -const _queueAsyncBuckets = new Map(); -const _gcLimit = 10000; - -async function _asyncQueueExecutor(queue, cleanup) { - let offt = 0; - while (true) { - let limit = Math.min(queue.length, _gcLimit); // Break up thundering hurds for GC duty. - for (let i = offt; i < limit; i++) { - const job = queue[i]; - try { - job.resolve(await job.awaitable()); - } catch (e) { - job.reject(e); - } - } - if (limit < queue.length) { - /* Perform lazy GC of queue for faster iteration. */ - if (limit >= _gcLimit) { - queue.splice(0, limit); - offt = 0; - } else { - offt = limit; - } - } else { - break; - } - } - cleanup(); -} - -module.exports = function (bucket, awaitable) { - /* Run the async awaitable only when all other async calls registered - * here have completed (or thrown). The bucket argument is a hashable - * key representing the task queue to use. */ - if (!awaitable.name) { - // Make debuging easier by adding a name to this function. - Object.defineProperty(awaitable, 'name', { writable: true }); - if (typeof bucket === 'string') { - awaitable.name = bucket; - } else { - console.warn("Unhandled bucket type (for naming):", typeof bucket, bucket); - } - } - let inactive; - if (!_queueAsyncBuckets.has(bucket)) { - _queueAsyncBuckets.set(bucket, []); - inactive = true; - } - const queue = _queueAsyncBuckets.get(bucket); - const job = new Promise((resolve, reject) => queue.push({ - awaitable, - resolve, - reject - })); - if (inactive) { - /* An executor is not currently active; Start one now. */ - _asyncQueueExecutor(queue, () => _queueAsyncBuckets.delete(bucket)); - } - return job; -}; \ No newline at end of file diff --git a/WASignalGroup/readme.md b/WASignalGroup/readme.md deleted file mode 100644 index 8290d9c..0000000 --- a/WASignalGroup/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -# Signal-Group - -This contains the code to decrypt/encrypt WA group messages. -Originally from [pokearaujo/libsignal-node](https://github.com/pokearaujo/libsignal-node) - -The code has been moved outside the signal package as I felt it didn't belong in ths signal package, as it isn't inherently a part of signal but of WA. \ No newline at end of file diff --git a/WASignalGroup/sender_chain_key.js b/WASignalGroup/sender_chain_key.js deleted file mode 100644 index 9a3125b..0000000 --- a/WASignalGroup/sender_chain_key.js +++ /dev/null @@ -1,50 +0,0 @@ -const SenderMessageKey = require('./sender_message_key'); -//const HKDF = require('./hkdf'); -const crypto = require('libsignal/src/crypto'); - -class SenderChainKey { - MESSAGE_KEY_SEED = Buffer.from([0x01]); - - CHAIN_KEY_SEED = Buffer.from([0x02]); - - iteration = 0; - - chainKey = Buffer.alloc(0); - - constructor(iteration, chainKey) { - this.iteration = iteration; - this.chainKey = chainKey; - } - - getIteration() { - return this.iteration; - } - - getSenderMessageKey() { - return new SenderMessageKey( - this.iteration, - this.getDerivative(this.MESSAGE_KEY_SEED, this.chainKey) - ); - } - - getNext() { - return new SenderChainKey( - this.iteration + 1, - this.getDerivative(this.CHAIN_KEY_SEED, this.chainKey) - ); - } - - getSeed() { - return typeof this.chainKey === 'string' ? Buffer.from(this.chainKey, 'base64') : this.chainKey; - } - - getDerivative(seed, key) { - key = typeof key === 'string' ? Buffer.from(key, 'base64') : key; - const hash = crypto.calculateMAC(key, seed); - //const hash = new Hash().hmac_hash(key, seed, 'sha256', ''); - - return hash; - } -} - -module.exports = SenderChainKey; \ No newline at end of file diff --git a/WASignalGroup/sender_key_distribution_message.js b/WASignalGroup/sender_key_distribution_message.js deleted file mode 100644 index 9c1400f..0000000 --- a/WASignalGroup/sender_key_distribution_message.js +++ /dev/null @@ -1,78 +0,0 @@ -const CiphertextMessage = require('./ciphertext_message'); -const protobufs = require('./protobufs'); - -class SenderKeyDistributionMessage extends CiphertextMessage { - constructor( - id = null, - iteration = null, - chainKey = null, - signatureKey = null, - serialized = null - ) { - super(); - if (serialized) { - try { - const version = serialized[0]; - const message = serialized.slice(1); - - const distributionMessage = protobufs.SenderKeyDistributionMessage.decode( - message - ).toJSON(); - this.serialized = serialized; - this.id = distributionMessage.id; - this.iteration = distributionMessage.iteration; - this.chainKey = distributionMessage.chainKey; - this.signatureKey = distributionMessage.signingKey; - } catch (e) { - throw new Error(e); - } - } else { - const version = this.intsToByteHighAndLow(this.CURRENT_VERSION, this.CURRENT_VERSION); - this.id = id; - this.iteration = iteration; - this.chainKey = chainKey; - this.signatureKey = signatureKey; - const message = protobufs.SenderKeyDistributionMessage.encode( - protobufs.SenderKeyDistributionMessage.create({ - id, - iteration, - chainKey, - signingKey: this.signatureKey, - }) - ).finish(); - this.serialized = Buffer.concat([Buffer.from([version]), message]); - } - } - - intsToByteHighAndLow(highValue, lowValue) { - return (((highValue << 4) | lowValue) & 0xff) % 256; - } - - serialize() { - return this.serialized; - } - - getType() { - return this.SENDERKEY_DISTRIBUTION_TYPE; - } - - getIteration() { - return this.iteration; - } - - getChainKey() { - return typeof this.chainKey === 'string' ? Buffer.from(this.chainKey, 'base64') : this.chainKey; - } - - getSignatureKey() { - return typeof this.signatureKey === 'string' - ? Buffer.from(this.signatureKey, 'base64') - : this.signatureKey; - } - - getId() { - return this.id; - } -} - -module.exports = SenderKeyDistributionMessage; \ No newline at end of file diff --git a/WASignalGroup/sender_key_message.js b/WASignalGroup/sender_key_message.js deleted file mode 100644 index 288aeba..0000000 --- a/WASignalGroup/sender_key_message.js +++ /dev/null @@ -1,92 +0,0 @@ -const CiphertextMessage = require('./ciphertext_message'); -const curve = require('libsignal/src/curve'); -const protobufs = require('./protobufs'); - -class SenderKeyMessage extends CiphertextMessage { - SIGNATURE_LENGTH = 64; - - constructor( - keyId = null, - iteration = null, - ciphertext = null, - signatureKey = null, - serialized = null - ) { - super(); - if (serialized) { - const version = serialized[0]; - const message = serialized.slice(1, serialized.length - this.SIGNATURE_LENGTH); - const signature = serialized.slice(-1 * this.SIGNATURE_LENGTH); - const senderKeyMessage = protobufs.SenderKeyMessage.decode(message).toJSON(); - senderKeyMessage.ciphertext = Buffer.from(senderKeyMessage.ciphertext, 'base64'); - - this.serialized = serialized; - this.messageVersion = (version & 0xff) >> 4; - - this.keyId = senderKeyMessage.id; - this.iteration = senderKeyMessage.iteration; - this.ciphertext = senderKeyMessage.ciphertext; - this.signature = signature; - } else { - const version = (((this.CURRENT_VERSION << 4) | this.CURRENT_VERSION) & 0xff) % 256; - ciphertext = Buffer.from(ciphertext); // .toString('base64'); - const message = protobufs.SenderKeyMessage.encode( - protobufs.SenderKeyMessage.create({ - id: keyId, - iteration, - ciphertext, - }) - ).finish(); - - const signature = this.getSignature( - signatureKey, - Buffer.concat([Buffer.from([version]), message]) - ); - this.serialized = Buffer.concat([Buffer.from([version]), message, Buffer.from(signature)]); - this.messageVersion = this.CURRENT_VERSION; - this.keyId = keyId; - this.iteration = iteration; - this.ciphertext = ciphertext; - this.signature = signature; - } - } - - getKeyId() { - return this.keyId; - } - - getIteration() { - return this.iteration; - } - - getCipherText() { - return this.ciphertext; - } - - verifySignature(signatureKey) { - const part1 = this.serialized.slice(0, this.serialized.length - this.SIGNATURE_LENGTH); - const part2 = this.serialized.slice(-1 * this.SIGNATURE_LENGTH); - const res = curve.verifySignature(signatureKey, part1, part2); - if (!res) throw new Error('Invalid signature!'); - } - - getSignature(signatureKey, serialized) { - const signature = Buffer.from( - curve.calculateSignature( - signatureKey, - serialized - ) - ); - return signature; - } - - serialize() { - return this.serialized; - } - - getType() { - return 4; - } -} - -module.exports = SenderKeyMessage; \ No newline at end of file diff --git a/WASignalGroup/sender_key_name.js b/WASignalGroup/sender_key_name.js deleted file mode 100644 index e7f5290..0000000 --- a/WASignalGroup/sender_key_name.js +++ /dev/null @@ -1,70 +0,0 @@ -function isNull(str) { - return str === null || str.value === ''; -} - -/** - * java String hashCode 的实现 - * @param strKey - * @return intValue - */ -function intValue(num) { - const MAX_VALUE = 0x7fffffff; - const MIN_VALUE = -0x80000000; - if (num > MAX_VALUE || num < MIN_VALUE) { - // eslint-disable-next-line - return (num &= 0xffffffff); - } - return num; -} - -function hashCode(strKey) { - let hash = 0; - if (!isNull(strKey)) { - for (let i = 0; i < strKey.length; i++) { - hash = hash * 31 + strKey.charCodeAt(i); - hash = intValue(hash); - } - } - return hash; -} - -/** - * 将js页面的number类型转换为java的int类型 - * @param num - * @return intValue - */ - -class SenderKeyName { - constructor(groupId, sender) { - this.groupId = groupId; - this.sender = sender; - } - - getGroupId() { - return this.groupId; - } - - getSender() { - return this.sender; - } - - serialize() { - return `${this.groupId}::${this.sender.id}::${this.sender.deviceId}`; - } - - toString() { - return this.serialize(); - } - - equals(other) { - if (other === null) return false; - if (!(other instanceof SenderKeyName)) return false; - return this.groupId === other.groupId && this.sender.toString() === other.sender.toString(); - } - - hashCode() { - return hashCode(this.groupId) ^ hashCode(this.sender.toString()); - } -} - -module.exports = SenderKeyName; \ No newline at end of file diff --git a/WASignalGroup/sender_key_record.js b/WASignalGroup/sender_key_record.js deleted file mode 100644 index 8112efa..0000000 --- a/WASignalGroup/sender_key_record.js +++ /dev/null @@ -1,56 +0,0 @@ -const SenderKeyState = require('./sender_key_state'); - -class SenderKeyRecord { - MAX_STATES = 5; - - constructor(serialized) { - this.senderKeyStates = []; - - if (serialized) { - const list = serialized; - for (let i = 0; i < list.length; i++) { - const structure = list[i]; - this.senderKeyStates.push( - new SenderKeyState(null, null, null, null, null, null, structure) - ); - } - } - } - - isEmpty() { - return this.senderKeyStates.length === 0; - } - - getSenderKeyState(keyId) { - if (!keyId && this.senderKeyStates.length) return this.senderKeyStates[this.senderKeyStates.length - 1]; - for (let i = 0; i < this.senderKeyStates.length; i++) { - const state = this.senderKeyStates[i]; - if (state.getKeyId() === keyId) { - return state; - } - } - } - - addSenderKeyState(id, iteration, chainKey, signatureKey) { - this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, null, signatureKey)); - if (this.senderKeyStates.length > 5) { - this.senderKeyStates.shift() - } - } - - setSenderKeyState(id, iteration, chainKey, keyPair) { - this.senderKeyStates.length = 0; - this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, keyPair)); - } - - serialize() { - const recordStructure = []; - for (let i = 0; i < this.senderKeyStates.length; i++) { - const senderKeyState = this.senderKeyStates[i]; - recordStructure.push(senderKeyState.getStructure()); - } - return recordStructure; - } - } - - module.exports = SenderKeyRecord; \ No newline at end of file diff --git a/WASignalGroup/sender_key_state.js b/WASignalGroup/sender_key_state.js deleted file mode 100644 index c3e0fc9..0000000 --- a/WASignalGroup/sender_key_state.js +++ /dev/null @@ -1,129 +0,0 @@ -const SenderChainKey = require('./sender_chain_key'); -const SenderMessageKey = require('./sender_message_key'); - -const protobufs = require('./protobufs'); - -class SenderKeyState { - MAX_MESSAGE_KEYS = 2000; - - constructor( - id = null, - iteration = null, - chainKey = null, - signatureKeyPair = null, - signatureKeyPublic = null, - signatureKeyPrivate = null, - senderKeyStateStructure = null - ) { - if (senderKeyStateStructure) { - this.senderKeyStateStructure = senderKeyStateStructure; - } else { - if (signatureKeyPair) { - signatureKeyPublic = signatureKeyPair.public; - signatureKeyPrivate = signatureKeyPair.private; - } - - chainKey = typeof chainKey === 'string' ? Buffer.from(chainKey, 'base64') : chainKey; - this.senderKeyStateStructure = protobufs.SenderKeyStateStructure.create(); - const senderChainKeyStructure = protobufs.SenderChainKey.create(); - senderChainKeyStructure.iteration = iteration; - senderChainKeyStructure.seed = chainKey; - this.senderKeyStateStructure.senderChainKey = senderChainKeyStructure; - - const signingKeyStructure = protobufs.SenderSigningKey.create(); - signingKeyStructure.public = - typeof signatureKeyPublic === 'string' ? - Buffer.from(signatureKeyPublic, 'base64') : - signatureKeyPublic; - if (signatureKeyPrivate) { - signingKeyStructure.private = - typeof signatureKeyPrivate === 'string' ? - Buffer.from(signatureKeyPrivate, 'base64') : - signatureKeyPrivate; - } - this.senderKeyStateStructure.senderKeyId = id; - this.senderChainKey = senderChainKeyStructure; - this.senderKeyStateStructure.senderSigningKey = signingKeyStructure; - } - this.senderKeyStateStructure.senderMessageKeys = - this.senderKeyStateStructure.senderMessageKeys || []; - } - - SenderKeyState(senderKeyStateStructure) { - this.senderKeyStateStructure = senderKeyStateStructure; - } - - getKeyId() { - return this.senderKeyStateStructure.senderKeyId; - } - - getSenderChainKey() { - return new SenderChainKey( - this.senderKeyStateStructure.senderChainKey.iteration, - this.senderKeyStateStructure.senderChainKey.seed - ); - } - - setSenderChainKey(chainKey) { - const senderChainKeyStructure = protobufs.SenderChainKey.create({ - iteration: chainKey.getIteration(), - seed: chainKey.getSeed(), - }); - this.senderKeyStateStructure.senderChainKey = senderChainKeyStructure; - } - - getSigningKeyPublic() { - return typeof this.senderKeyStateStructure.senderSigningKey.public === 'string' ? - Buffer.from(this.senderKeyStateStructure.senderSigningKey.public, 'base64') : - this.senderKeyStateStructure.senderSigningKey.public; - } - - getSigningKeyPrivate() { - return typeof this.senderKeyStateStructure.senderSigningKey.private === 'string' ? - Buffer.from(this.senderKeyStateStructure.senderSigningKey.private, 'base64') : - this.senderKeyStateStructure.senderSigningKey.private; - } - - hasSenderMessageKey(iteration) { - const list = this.senderKeyStateStructure.senderMessageKeys; - for (let o = 0; o < list.length; o++) { - const senderMessageKey = list[o]; - if (senderMessageKey.iteration === iteration) return true; - } - return false; - } - - addSenderMessageKey(senderMessageKey) { - const senderMessageKeyStructure = protobufs.SenderKeyStateStructure.create({ - iteration: senderMessageKey.getIteration(), - seed: senderMessageKey.getSeed(), - }); - this.senderKeyStateStructure.senderMessageKeys.push(senderMessageKeyStructure); - - if (this.senderKeyStateStructure.senderMessageKeys.length > this.MAX_MESSAGE_KEYS) { - this.senderKeyStateStructure.senderMessageKeys.shift(); - } - } - - removeSenderMessageKey(iteration) { - let result = null; - - this.senderKeyStateStructure.senderMessageKeys = this.senderKeyStateStructure.senderMessageKeys.filter( - senderMessageKey => { - if (senderMessageKey.iteration === iteration) result = senderMessageKey; - return senderMessageKey.iteration !== iteration; - } - ); - - if (result != null) { - return new SenderMessageKey(result.iteration, result.seed); - } - return null; - } - - getStructure() { - return this.senderKeyStateStructure; - } -} - -module.exports = SenderKeyState; \ No newline at end of file diff --git a/WASignalGroup/sender_message_key.js b/WASignalGroup/sender_message_key.js deleted file mode 100644 index 7639704..0000000 --- a/WASignalGroup/sender_message_key.js +++ /dev/null @@ -1,39 +0,0 @@ -const { deriveSecrets } = require('libsignal/src/crypto'); -class SenderMessageKey { - iteration = 0; - - iv = Buffer.alloc(0); - - cipherKey = Buffer.alloc(0); - - seed = Buffer.alloc(0); - - constructor(iteration, seed) { - const derivative = deriveSecrets(seed, Buffer.alloc(32), Buffer.from('WhisperGroup')); - const keys = new Uint8Array(32); - keys.set(new Uint8Array(derivative[0].slice(16))); - keys.set(new Uint8Array(derivative[1].slice(0, 16)), 16); - this.iv = Buffer.from(derivative[0].slice(0, 16)); - this.cipherKey = Buffer.from(keys.buffer); - - this.iteration = iteration; - this.seed = seed; - } - - getIteration() { - return this.iteration; - } - - getIv() { - return this.iv; - } - - getCipherKey() { - return this.cipherKey; - } - - getSeed() { - return this.seed; - } -} -module.exports = SenderMessageKey; \ No newline at end of file diff --git a/package.json b/package.json index fc8dcd7..1a43d5e 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,7 @@ "types": "lib/index.d.ts", "files": [ "lib/*", - "WAProto/*.ts", - "WAProto/*.js", - "WASignalGroup/*.js", + "WAProto/*", "engine-requirements.js" ], "scripts": { diff --git a/src/Signal/Group/ciphertext-message.ts b/src/Signal/Group/ciphertext-message.ts new file mode 100644 index 0000000..238e015 --- /dev/null +++ b/src/Signal/Group/ciphertext-message.ts @@ -0,0 +1,9 @@ +export class CiphertextMessage { + readonly UNSUPPORTED_VERSION: number = 1 + readonly CURRENT_VERSION: number = 3 + readonly WHISPER_TYPE: number = 2 + readonly PREKEY_TYPE: number = 3 + readonly SENDERKEY_TYPE: number = 4 + readonly SENDERKEY_DISTRIBUTION_TYPE: number = 5 + readonly ENCRYPTED_MESSAGE_OVERHEAD: number = 53 +} diff --git a/src/Signal/Group/group-session-builder.ts b/src/Signal/Group/group-session-builder.ts new file mode 100644 index 0000000..b2a90b6 --- /dev/null +++ b/src/Signal/Group/group-session-builder.ts @@ -0,0 +1,56 @@ +import * as keyhelper from './keyhelper' +import { SenderKeyDistributionMessage } from './sender-key-distribution-message' +import { SenderKeyName } from './sender-key-name' +import { SenderKeyRecord } from './sender-key-record' + +interface SenderKeyStore { + loadSenderKey(senderKeyName: SenderKeyName): Promise + storeSenderKey(senderKeyName: SenderKeyName, record: SenderKeyRecord): Promise +} + +export class GroupSessionBuilder { + private readonly senderKeyStore: SenderKeyStore + + constructor(senderKeyStore: SenderKeyStore) { + this.senderKeyStore = senderKeyStore + } + + public async process( + senderKeyName: SenderKeyName, + senderKeyDistributionMessage: SenderKeyDistributionMessage + ): Promise { + const senderKeyRecord = await this.senderKeyStore.loadSenderKey(senderKeyName) + senderKeyRecord.addSenderKeyState( + senderKeyDistributionMessage.getId(), + senderKeyDistributionMessage.getIteration(), + senderKeyDistributionMessage.getChainKey(), + senderKeyDistributionMessage.getSignatureKey() + ) + await this.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord) + } + + public async create(senderKeyName: SenderKeyName): Promise { + const senderKeyRecord = await this.senderKeyStore.loadSenderKey(senderKeyName) + + if (senderKeyRecord.isEmpty()) { + const keyId = keyhelper.generateSenderKeyId() + const senderKey = keyhelper.generateSenderKey() + const signingKey = keyhelper.generateSenderSigningKey() + + senderKeyRecord.setSenderKeyState(keyId, 0, senderKey, signingKey) + await this.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord) + } + + const state = senderKeyRecord.getSenderKeyState() + if (!state) { + throw new Error('No session state available') + } + + return new SenderKeyDistributionMessage( + state.getKeyId(), + state.getSenderChainKey().getIteration(), + state.getSenderChainKey().getSeed(), + state.getSigningKeyPublic() + ) + } +} diff --git a/src/Signal/Group/group_cipher.ts b/src/Signal/Group/group_cipher.ts new file mode 100644 index 0000000..c04e0f0 --- /dev/null +++ b/src/Signal/Group/group_cipher.ts @@ -0,0 +1,132 @@ +import { decrypt, encrypt } from 'libsignal/src/crypto' +import queueJob from './queue-job' +import { SenderKeyMessage } from './sender-key-message' +import { SenderKeyName } from './sender-key-name' +import { SenderKeyRecord } from './sender-key-record' +import { SenderKeyState } from './sender-key-state' + +export interface SenderKeyStore { + loadSenderKey(senderKeyName: SenderKeyName): Promise + storeSenderKey(senderKeyName: SenderKeyName, record: SenderKeyRecord): Promise +} + +export class GroupCipher { + private readonly senderKeyStore: SenderKeyStore + private readonly senderKeyName: SenderKeyName + + constructor(senderKeyStore: SenderKeyStore, senderKeyName: SenderKeyName) { + this.senderKeyStore = senderKeyStore + this.senderKeyName = senderKeyName + } + + private queueJob(awaitable: () => Promise): Promise { + return queueJob(this.senderKeyName.toString(), awaitable) + } + + public async encrypt(paddedPlaintext: Uint8Array | string): Promise { + return await this.queueJob(async () => { + const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName) + if (!record) { + throw new Error('No SenderKeyRecord found for encryption') + } + + const senderKeyState = record.getSenderKeyState() + if (!senderKeyState) { + throw new Error('No session to encrypt message') + } + + const iteration = senderKeyState.getSenderChainKey().getIteration() + const senderKey = this.getSenderKey(senderKeyState, iteration === 0 ? 0 : iteration + 1) + + const ciphertext = await this.getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext) + + const senderKeyMessage = new SenderKeyMessage( + senderKeyState.getKeyId(), + senderKey.getIteration(), + ciphertext, + senderKeyState.getSigningKeyPrivate() + ) + + await this.senderKeyStore.storeSenderKey(this.senderKeyName, record) + return senderKeyMessage.serialize() + }) + } + + public async decrypt(senderKeyMessageBytes: Uint8Array): Promise { + return await this.queueJob(async () => { + const record = await this.senderKeyStore.loadSenderKey(this.senderKeyName) + if (!record) { + throw new Error('No SenderKeyRecord found for decryption') + } + + const senderKeyMessage = new SenderKeyMessage(null, null, null, null, senderKeyMessageBytes) + const senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()) + if (!senderKeyState) { + throw new Error('No session found to decrypt message') + } + + senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()) + const senderKey = this.getSenderKey(senderKeyState, senderKeyMessage.getIteration()) + + const plaintext = await this.getPlainText( + senderKey.getIv(), + senderKey.getCipherKey(), + senderKeyMessage.getCipherText() + ) + + await this.senderKeyStore.storeSenderKey(this.senderKeyName, record) + return plaintext + }) + } + + private getSenderKey(senderKeyState: SenderKeyState, iteration: number) { + let senderChainKey = senderKeyState.getSenderChainKey() + if (senderChainKey.getIteration() > iteration) { + if (senderKeyState.hasSenderMessageKey(iteration)) { + const messageKey = senderKeyState.removeSenderMessageKey(iteration) + if (!messageKey) { + throw new Error('No sender message key found for iteration') + } + + return messageKey + } + + throw new Error(`Received message with old counter: ${senderChainKey.getIteration()}, ${iteration}`) + } + + if (iteration - senderChainKey.getIteration() > 2000) { + throw new Error('Over 2000 messages into the future!') + } + + while (senderChainKey.getIteration() < iteration) { + senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()) + senderChainKey = senderChainKey.getNext() + } + + senderKeyState.setSenderChainKey(senderChainKey.getNext()) + return senderChainKey.getSenderMessageKey() + } + + private async getPlainText(iv: Uint8Array, key: Uint8Array, ciphertext: Uint8Array): Promise { + try { + return decrypt(key, ciphertext, iv) + } catch (e) { + throw new Error('InvalidMessageException') + } + } + + private async getCipherText( + iv: Uint8Array | string, + key: Uint8Array | string, + plaintext: Uint8Array | string + ): Promise { + try { + const ivBuffer = typeof iv === 'string' ? Buffer.from(iv, 'base64') : iv + const keyBuffer = typeof key === 'string' ? Buffer.from(key, 'base64') : key + const plaintextBuffer = typeof plaintext === 'string' ? Buffer.from(plaintext) : plaintext + return encrypt(keyBuffer, plaintextBuffer, ivBuffer) + } catch (e) { + throw new Error('InvalidMessageException') + } + } +} diff --git a/src/Signal/Group/index.ts b/src/Signal/Group/index.ts new file mode 100644 index 0000000..52c983d --- /dev/null +++ b/src/Signal/Group/index.ts @@ -0,0 +1,11 @@ +export { GroupSessionBuilder } from './group-session-builder' +export { SenderKeyDistributionMessage } from './sender-key-distribution-message' +export { SenderKeyRecord } from './sender-key-record' +export { SenderKeyName } from './sender-key-name' +export { GroupCipher } from './group_cipher' +export { SenderKeyState } from './sender-key-state' +export { SenderKeyMessage } from './sender-key-message' +export { SenderMessageKey } from './sender-message-key' +export { SenderChainKey } from './sender-chain-key' +export { CiphertextMessage } from './ciphertext-message' +export * as keyhelper from './keyhelper' diff --git a/src/Signal/Group/keyhelper.ts b/src/Signal/Group/keyhelper.ts new file mode 100644 index 0000000..acf274c --- /dev/null +++ b/src/Signal/Group/keyhelper.ts @@ -0,0 +1,28 @@ +import * as nodeCrypto from 'crypto' +import { generateKeyPair } from 'libsignal/src/curve' + +type KeyPairType = ReturnType + +export function generateSenderKey(): Buffer { + return nodeCrypto.randomBytes(32) +} + +export function generateSenderKeyId(): number { + return nodeCrypto.randomInt(2147483647) +} + +export interface SigningKeyPair { + public: Buffer + private: Buffer +} + +export function generateSenderSigningKey(key?: KeyPairType): SigningKeyPair { + if (!key) { + key = generateKeyPair() + } + + return { + public: Buffer.from(key.pubKey), + private: Buffer.from(key.privKey) + } +} diff --git a/src/Signal/Group/queue-job.ts b/src/Signal/Group/queue-job.ts new file mode 100644 index 0000000..9b71144 --- /dev/null +++ b/src/Signal/Group/queue-job.ts @@ -0,0 +1,65 @@ +interface QueueJob { + awaitable: () => Promise + resolve: (value: T | PromiseLike) => void + reject: (reason?: unknown) => void +} + +const _queueAsyncBuckets = new Map>>() +const _gcLimit = 10000 + +async function _asyncQueueExecutor(queue: Array>, cleanup: () => void): Promise { + let offt = 0 + // eslint-disable-next-line no-constant-condition + while (true) { + const limit = Math.min(queue.length, _gcLimit) + for (let i = offt; i < limit; i++) { + const job = queue[i] + try { + job.resolve(await job.awaitable()) + } catch (e) { + job.reject(e) + } + } + + if (limit < queue.length) { + if (limit >= _gcLimit) { + queue.splice(0, limit) + offt = 0 + } else { + offt = limit + } + } else { + break + } + } + + cleanup() +} + +export default function queueJob(bucket: string | number, awaitable: () => Promise): Promise { + // Skip name assignment since it's readonly in strict mode + if (typeof bucket !== 'string') { + console.warn('Unhandled bucket type (for naming):', typeof bucket, bucket) + } + + let inactive = false + if (!_queueAsyncBuckets.has(bucket)) { + _queueAsyncBuckets.set(bucket, []) + inactive = true + } + + const queue = _queueAsyncBuckets.get(bucket)! + const job = new Promise((resolve, reject) => { + queue.push({ + awaitable, + resolve: resolve as (value: any) => void, + reject + }) + }) + + if (inactive) { + _asyncQueueExecutor(queue, () => _queueAsyncBuckets.delete(bucket)) + } + + return job +} diff --git a/src/Signal/Group/sender-chain-key.ts b/src/Signal/Group/sender-chain-key.ts new file mode 100644 index 0000000..ba90037 --- /dev/null +++ b/src/Signal/Group/sender-chain-key.ts @@ -0,0 +1,38 @@ +import { calculateMAC } from 'libsignal/src/crypto' +import { SenderMessageKey } from './sender-message-key' + +export class SenderChainKey { + private readonly MESSAGE_KEY_SEED: Uint8Array = Buffer.from([0x01]) + private readonly CHAIN_KEY_SEED: Uint8Array = Buffer.from([0x02]) + private readonly iteration: number + private readonly chainKey: Buffer + + constructor(iteration: number, chainKey: any) { + this.iteration = iteration + if (chainKey instanceof Buffer) { + this.chainKey = chainKey + } else { + this.chainKey = Buffer.from(chainKey || []) + } + } + + public getIteration(): number { + return this.iteration + } + + public getSenderMessageKey(): SenderMessageKey { + return new SenderMessageKey(this.iteration, this.getDerivative(this.MESSAGE_KEY_SEED, this.chainKey)) + } + + public getNext(): SenderChainKey { + return new SenderChainKey(this.iteration + 1, this.getDerivative(this.CHAIN_KEY_SEED, this.chainKey)) + } + + public getSeed(): Uint8Array { + return this.chainKey + } + + private getDerivative(seed: Uint8Array, key: Buffer): Uint8Array { + return calculateMAC(key, seed) + } +} diff --git a/src/Signal/Group/sender-key-distribution-message.ts b/src/Signal/Group/sender-key-distribution-message.ts new file mode 100644 index 0000000..b472936 --- /dev/null +++ b/src/Signal/Group/sender-key-distribution-message.ts @@ -0,0 +1,95 @@ +import { proto } from '../../../WAProto' +import { CiphertextMessage } from './ciphertext-message' + +interface SenderKeyDistributionMessageStructure { + id: number + iteration: number + chainKey: string | Uint8Array + signingKey: string | Uint8Array +} + +export class SenderKeyDistributionMessage extends CiphertextMessage { + private readonly id: number + private readonly iteration: number + private readonly chainKey: Uint8Array + private readonly signatureKey: Uint8Array + private readonly serialized: Uint8Array + + constructor( + id?: number | null, + iteration?: number | null, + chainKey?: Uint8Array | null, + signatureKey?: Uint8Array | null, + serialized?: Uint8Array | null + ) { + super() + + if (serialized) { + try { + const message = serialized.slice(1) + const distributionMessage = proto.SenderKeyDistributionMessage.decode( + message + ).toJSON() as SenderKeyDistributionMessageStructure + + this.serialized = serialized + this.id = distributionMessage.id + this.iteration = distributionMessage.iteration + this.chainKey = + typeof distributionMessage.chainKey === 'string' + ? Buffer.from(distributionMessage.chainKey, 'base64') + : distributionMessage.chainKey + this.signatureKey = + typeof distributionMessage.signingKey === 'string' + ? Buffer.from(distributionMessage.signingKey, 'base64') + : distributionMessage.signingKey + } catch (e) { + throw new Error(String(e)) + } + } else { + const version = this.intsToByteHighAndLow(this.CURRENT_VERSION, this.CURRENT_VERSION) + this.id = id! + this.iteration = iteration! + this.chainKey = chainKey! + this.signatureKey = signatureKey! + + const message = proto.SenderKeyDistributionMessage.encode( + proto.SenderKeyDistributionMessage.create({ + id, + iteration, + chainKey, + signingKey: this.signatureKey + }) + ).finish() + + this.serialized = Buffer.concat([Buffer.from([version]), message]) + } + } + + private intsToByteHighAndLow(highValue: number, lowValue: number): number { + return (((highValue << 4) | lowValue) & 0xff) % 256 + } + + public serialize(): Uint8Array { + return this.serialized + } + + public getType(): number { + return this.SENDERKEY_DISTRIBUTION_TYPE + } + + public getIteration(): number { + return this.iteration + } + + public getChainKey(): Uint8Array { + return typeof this.chainKey === 'string' ? Buffer.from(this.chainKey, 'base64') : this.chainKey + } + + public getSignatureKey(): Uint8Array { + return typeof this.signatureKey === 'string' ? Buffer.from(this.signatureKey, 'base64') : this.signatureKey + } + + public getId(): number { + return this.id + } +} diff --git a/src/Signal/Group/sender-key-message.ts b/src/Signal/Group/sender-key-message.ts new file mode 100644 index 0000000..4efde96 --- /dev/null +++ b/src/Signal/Group/sender-key-message.ts @@ -0,0 +1,96 @@ +import { calculateSignature, verifySignature } from 'libsignal/src/curve' +import { proto } from '../../../WAProto' +import { CiphertextMessage } from './ciphertext-message' + +interface SenderKeyMessageStructure { + id: number + iteration: number + ciphertext: string | Buffer +} + +export class SenderKeyMessage extends CiphertextMessage { + private readonly SIGNATURE_LENGTH = 64 + private readonly messageVersion: number + private readonly keyId: number + private readonly iteration: number + private readonly ciphertext: Uint8Array + private readonly signature: Uint8Array + private readonly serialized: Uint8Array + + constructor( + keyId?: number | null, + iteration?: number | null, + ciphertext?: Uint8Array | null, + signatureKey?: Uint8Array | null, + serialized?: Uint8Array | null + ) { + super() + + if (serialized) { + const version = serialized[0] + const message = serialized.slice(1, serialized.length - this.SIGNATURE_LENGTH) + const signature = serialized.slice(-1 * this.SIGNATURE_LENGTH) + const senderKeyMessage = proto.SenderKeyMessage.decode(message).toJSON() as SenderKeyMessageStructure + + this.serialized = serialized + this.messageVersion = (version & 0xff) >> 4 + this.keyId = senderKeyMessage.id + this.iteration = senderKeyMessage.iteration + this.ciphertext = + typeof senderKeyMessage.ciphertext === 'string' + ? Buffer.from(senderKeyMessage.ciphertext, 'base64') + : senderKeyMessage.ciphertext + this.signature = signature + } else { + const version = (((this.CURRENT_VERSION << 4) | this.CURRENT_VERSION) & 0xff) % 256 + const ciphertextBuffer = Buffer.from(ciphertext!) + const message = proto.SenderKeyMessage.encode( + proto.SenderKeyMessage.create({ + id: keyId!, + iteration: iteration!, + ciphertext: ciphertextBuffer + }) + ).finish() + + const signature = this.getSignature(signatureKey!, Buffer.concat([Buffer.from([version]), message])) + + this.serialized = Buffer.concat([Buffer.from([version]), message, Buffer.from(signature)]) + this.messageVersion = this.CURRENT_VERSION + this.keyId = keyId! + this.iteration = iteration! + this.ciphertext = ciphertextBuffer + this.signature = signature + } + } + + public getKeyId(): number { + return this.keyId + } + + public getIteration(): number { + return this.iteration + } + + public getCipherText(): Uint8Array { + return this.ciphertext + } + + public verifySignature(signatureKey: Uint8Array): void { + const part1 = this.serialized.slice(0, this.serialized.length - this.SIGNATURE_LENGTH) + const part2 = this.serialized.slice(-1 * this.SIGNATURE_LENGTH) + const res = verifySignature(signatureKey, part1, part2) + if (!res) throw new Error('Invalid signature!') + } + + private getSignature(signatureKey: Uint8Array, serialized: Uint8Array): Uint8Array { + return Buffer.from(calculateSignature(signatureKey, serialized)) + } + + public serialize(): Uint8Array { + return this.serialized + } + + public getType(): number { + return 4 + } +} diff --git a/src/Signal/Group/sender-key-name.ts b/src/Signal/Group/sender-key-name.ts new file mode 100644 index 0000000..0948687 --- /dev/null +++ b/src/Signal/Group/sender-key-name.ts @@ -0,0 +1,66 @@ +interface Sender { + id: string + deviceId: number + toString(): string +} + +function isNull(str: string | null): boolean { + return str === null || str === '' +} + +function intValue(num: number): number { + const MAX_VALUE = 0x7fffffff + const MIN_VALUE = -0x80000000 + if (num > MAX_VALUE || num < MIN_VALUE) { + return num & 0xffffffff + } + + return num +} + +function hashCode(strKey: string): number { + let hash = 0 + if (!isNull(strKey)) { + for (let i = 0; i < strKey.length; i++) { + hash = hash * 31 + strKey.charCodeAt(i) + hash = intValue(hash) + } + } + + return hash +} + +export class SenderKeyName { + private readonly groupId: string + private readonly sender: Sender + + constructor(groupId: string, sender: Sender) { + this.groupId = groupId + this.sender = sender + } + + public getGroupId(): string { + return this.groupId + } + + public getSender(): Sender { + return this.sender + } + + public serialize(): string { + return `${this.groupId}::${this.sender.id}::${this.sender.deviceId}` + } + + public toString(): string { + return this.serialize() + } + + public equals(other: SenderKeyName | null): boolean { + if (other === null) return false + return this.groupId === other.groupId && this.sender.toString() === other.sender.toString() + } + + public hashCode(): number { + return hashCode(this.groupId) ^ hashCode(this.sender.toString()) + } +} diff --git a/src/Signal/Group/sender-key-record.ts b/src/Signal/Group/sender-key-record.ts new file mode 100644 index 0000000..de30182 --- /dev/null +++ b/src/Signal/Group/sender-key-record.ts @@ -0,0 +1,77 @@ +import { BufferJSON } from '../../Utils/generics' +import { SenderKeyState } from './sender-key-state' + +export interface SenderKeyStateStructure { + senderKeyId: number + senderChainKey: { + iteration: number + seed: Uint8Array + } + senderSigningKey: { + public: Uint8Array + private?: Uint8Array + } + senderMessageKeys: Array<{ + iteration: number + seed: Uint8Array + }> +} + +export class SenderKeyRecord { + private readonly MAX_STATES = 5 + private readonly senderKeyStates: SenderKeyState[] = [] + + constructor(serialized?: SenderKeyStateStructure[]) { + if (serialized) { + for (const structure of serialized) { + this.senderKeyStates.push(new SenderKeyState(null, null, null, null, null, null, structure)) + } + } + } + + public isEmpty(): boolean { + return this.senderKeyStates.length === 0 + } + + public getSenderKeyState(keyId?: number): SenderKeyState | undefined { + if (keyId === undefined && this.senderKeyStates.length) { + return this.senderKeyStates[this.senderKeyStates.length - 1] + } + + return this.senderKeyStates.find(state => state.getKeyId() === keyId) + } + + public addSenderKeyState(id: number, iteration: number, chainKey: Uint8Array, signatureKey: Uint8Array): void { + this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, null, signatureKey)) + if (this.senderKeyStates.length > this.MAX_STATES) { + this.senderKeyStates.shift() + } + } + + public setSenderKeyState( + id: number, + iteration: number, + chainKey: Uint8Array, + keyPair: { public: Uint8Array; private: Uint8Array } + ): void { + this.senderKeyStates.length = 0 + this.senderKeyStates.push(new SenderKeyState(id, iteration, chainKey, keyPair)) + } + + public serialize(): SenderKeyStateStructure[] { + return this.senderKeyStates.map(state => state.getStructure()) + } + static deserialize(data: Uint8Array | string | SenderKeyStateStructure[]): SenderKeyRecord { + let parsed: SenderKeyStateStructure[] + if (typeof data === 'string') { + parsed = JSON.parse(data, BufferJSON.reviver) + } else if (data instanceof Uint8Array) { + const str = Buffer.from(data).toString('utf-8') + parsed = JSON.parse(str, BufferJSON.reviver) + } else { + parsed = data + } + + return new SenderKeyRecord(parsed) + } +} diff --git a/src/Signal/Group/sender-key-state.ts b/src/Signal/Group/sender-key-state.ts new file mode 100644 index 0000000..3c0c7a6 --- /dev/null +++ b/src/Signal/Group/sender-key-state.ts @@ -0,0 +1,145 @@ +import { SenderChainKey } from './sender-chain-key' +import { SenderMessageKey } from './sender-message-key' + +interface SenderChainKeyStructure { + iteration: number + seed: Uint8Array +} + +interface SenderSigningKeyStructure { + public: Uint8Array + private?: Uint8Array +} + +interface SenderMessageKeyStructure { + iteration: number + seed: Uint8Array +} + +interface SenderKeyStateStructure { + senderKeyId: number + senderChainKey: SenderChainKeyStructure + senderSigningKey: SenderSigningKeyStructure + senderMessageKeys: SenderMessageKeyStructure[] +} + +export class SenderKeyState { + private readonly MAX_MESSAGE_KEYS = 2000 + private readonly senderKeyStateStructure: SenderKeyStateStructure + + constructor( + id?: number | null, + iteration?: number | null, + chainKey?: Uint8Array | null, + signatureKeyPair?: { public: Uint8Array; private: Uint8Array } | null, + signatureKeyPublic?: Uint8Array | null, + signatureKeyPrivate?: Uint8Array | null, + senderKeyStateStructure?: SenderKeyStateStructure | null + ) { + if (senderKeyStateStructure) { + this.senderKeyStateStructure = senderKeyStateStructure + } else { + if (signatureKeyPair) { + signatureKeyPublic = signatureKeyPair.public + signatureKeyPrivate = signatureKeyPair.private + } + + chainKey = typeof chainKey === 'string' ? Buffer.from(chainKey, 'base64') : chainKey + + const senderChainKeyStructure: SenderChainKeyStructure = { + iteration: iteration || 0, + seed: chainKey || Buffer.alloc(0) + } + + const signingKeyStructure: SenderSigningKeyStructure = { + public: + typeof signatureKeyPublic === 'string' + ? Buffer.from(signatureKeyPublic, 'base64') + : signatureKeyPublic || Buffer.alloc(0) + } + + if (signatureKeyPrivate) { + signingKeyStructure.private = + typeof signatureKeyPrivate === 'string' ? Buffer.from(signatureKeyPrivate, 'base64') : signatureKeyPrivate + } + + this.senderKeyStateStructure = { + senderKeyId: id || 0, + senderChainKey: senderChainKeyStructure, + senderSigningKey: signingKeyStructure, + senderMessageKeys: [] + } + } + } + + public getKeyId(): number { + return this.senderKeyStateStructure.senderKeyId + } + + public getSenderChainKey(): SenderChainKey { + return new SenderChainKey( + this.senderKeyStateStructure.senderChainKey.iteration, + this.senderKeyStateStructure.senderChainKey.seed + ) + } + + public setSenderChainKey(chainKey: SenderChainKey): void { + this.senderKeyStateStructure.senderChainKey = { + iteration: chainKey.getIteration(), + seed: chainKey.getSeed() + } + } + + public getSigningKeyPublic(): Buffer { + const publicKey = this.senderKeyStateStructure.senderSigningKey.public + if (publicKey instanceof Buffer) { + return publicKey + } + + return Buffer.from(publicKey || []) + } + + public getSigningKeyPrivate(): Buffer | undefined { + const privateKey = this.senderKeyStateStructure.senderSigningKey.private + if (!privateKey) { + return undefined + } + + if (privateKey instanceof Buffer) { + return privateKey + } + + return Buffer.from(privateKey) + } + + public hasSenderMessageKey(iteration: number): boolean { + return this.senderKeyStateStructure.senderMessageKeys.some(key => key.iteration === iteration) + } + + public addSenderMessageKey(senderMessageKey: SenderMessageKey): void { + this.senderKeyStateStructure.senderMessageKeys.push({ + iteration: senderMessageKey.getIteration(), + seed: senderMessageKey.getSeed() + }) + + if (this.senderKeyStateStructure.senderMessageKeys.length > this.MAX_MESSAGE_KEYS) { + this.senderKeyStateStructure.senderMessageKeys.shift() + } + } + + public removeSenderMessageKey(iteration: number): SenderMessageKey | null { + const index = this.senderKeyStateStructure.senderMessageKeys.findIndex(key => key.iteration === iteration) + + if (index !== -1) { + const messageKey = this.senderKeyStateStructure.senderMessageKeys[index] + this.senderKeyStateStructure.senderMessageKeys.splice(index, 1) + return new SenderMessageKey(messageKey.iteration, messageKey.seed) + } + + return null + } + + public getStructure(): SenderKeyStateStructure { + return this.senderKeyStateStructure + } +} diff --git a/src/Signal/Group/sender-message-key.ts b/src/Signal/Group/sender-message-key.ts new file mode 100644 index 0000000..7336a6e --- /dev/null +++ b/src/Signal/Group/sender-message-key.ts @@ -0,0 +1,36 @@ +import { deriveSecrets } from 'libsignal/src/crypto' + +export class SenderMessageKey { + private readonly iteration: number + private readonly iv: Uint8Array + private readonly cipherKey: Uint8Array + private readonly seed: Uint8Array + + constructor(iteration: number, seed: Uint8Array) { + const derivative = deriveSecrets(seed, Buffer.alloc(32), Buffer.from('WhisperGroup')) + const keys = new Uint8Array(32) + keys.set(new Uint8Array(derivative[0].slice(16))) + keys.set(new Uint8Array(derivative[1].slice(0, 16)), 16) + + this.iv = Buffer.from(derivative[0].slice(0, 16)) + this.cipherKey = Buffer.from(keys.buffer) + this.iteration = iteration + this.seed = seed + } + + public getIteration(): number { + return this.iteration + } + + public getIv(): Uint8Array { + return this.iv + } + + public getCipherKey(): Uint8Array { + return this.cipherKey + } + + public getSeed(): Uint8Array { + return this.seed + } +} diff --git a/src/Signal/libsignal.ts b/src/Signal/libsignal.ts index a80b08b..09d5724 100644 --- a/src/Signal/libsignal.ts +++ b/src/Signal/libsignal.ts @@ -1,18 +1,15 @@ import * as libsignal from 'libsignal' -import { - GroupCipher, - GroupSessionBuilder, - SenderKeyDistributionMessage, - SenderKeyName, - SenderKeyRecord -} from '../../WASignalGroup' import { SignalAuthState } from '../Types' import { SignalRepository } from '../Types/Signal' import { generateSignalPubKey } from '../Utils' import { jidDecode } from '../WABinary' +import type { SenderKeyStore } from './Group/group_cipher' +import { SenderKeyName } from './Group/sender-key-name' +import { SenderKeyRecord } from './Group/sender-key-record' +import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage } from './Group' export function makeLibSignalRepository(auth: SignalAuthState): SignalRepository { - const storage = signalStorage(auth) + const storage: SenderKeyStore = signalStorage(auth) return { decryptGroupMessage({ group, authorJid, msg }) { const senderName = jidToSignalSenderKeyName(group, authorJid) @@ -22,7 +19,11 @@ export function makeLibSignalRepository(auth: SignalAuthState): SignalRepository }, async processSenderKeyDistributionMessage({ item, authorJid }) { const builder = new GroupSessionBuilder(storage) - const senderName = jidToSignalSenderKeyName(item.groupId!, authorJid) + if (!item.groupId) { + throw new Error('Group ID is required for sender key distribution message') + } + + const senderName = jidToSignalSenderKeyName(item.groupId, authorJid) const senderMsg = new SenderKeyDistributionMessage( null, @@ -31,7 +32,8 @@ export function makeLibSignalRepository(auth: SignalAuthState): SignalRepository null, item.axolotlSenderKeyDistributionMessage ) - const { [senderName]: senderKey } = await auth.keys.get('sender-key', [senderName]) + const senderNameStr = senderName.toString() + const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]) if (!senderKey) { await storage.storeSenderKey(senderName, new SenderKeyRecord()) } @@ -49,6 +51,8 @@ export function makeLibSignalRepository(auth: SignalAuthState): SignalRepository case 'msg': result = await session.decryptWhisperMessage(ciphertext) break + default: + throw new Error(`Unknown message type: ${type}`) } return result @@ -65,7 +69,8 @@ export function makeLibSignalRepository(auth: SignalAuthState): SignalRepository const senderName = jidToSignalSenderKeyName(group, meId) const builder = new GroupSessionBuilder(storage) - const { [senderName]: senderKey } = await auth.keys.get('sender-key', [senderName]) + const senderNameStr = senderName.toString() + const { [senderNameStr]: senderKey } = await auth.keys.get('sender-key', [senderNameStr]) if (!senderKey) { await storage.storeSenderKey(senderName, new SenderKeyRecord()) } @@ -94,11 +99,11 @@ const jidToSignalProtocolAddress = (jid: string) => { return new libsignal.ProtocolAddress(user, device || 0) } -const jidToSignalSenderKeyName = (group: string, user: string): string => { - return new SenderKeyName(group, jidToSignalProtocolAddress(user)).toString() +const jidToSignalSenderKeyName = (group: string, user: string): SenderKeyName => { + return new SenderKeyName(group, jidToSignalProtocolAddress(user)) } -function signalStorage({ creds, keys }: SignalAuthState) { +function signalStorage({ creds, keys }: SignalAuthState): SenderKeyStore & Record { return { loadSession: async (id: string) => { const { [id]: sess } = await keys.get('session', [id]) @@ -106,7 +111,7 @@ function signalStorage({ creds, keys }: SignalAuthState) { return libsignal.SessionRecord.deserialize(sess) } }, - storeSession: async (id, session) => { + storeSession: async (id: string, session: libsignal.SessionRecord) => { await keys.set({ session: { [id]: session.serialize() } }) }, isTrustedIdentity: () => { @@ -130,14 +135,19 @@ function signalStorage({ creds, keys }: SignalAuthState) { pubKey: Buffer.from(key.keyPair.public) } }, - loadSenderKey: async (keyId: string) => { + loadSenderKey: async (senderKeyName: SenderKeyName) => { + const keyId = senderKeyName.toString() const { [keyId]: key } = await keys.get('sender-key', [keyId]) if (key) { - return new SenderKeyRecord(key) + return SenderKeyRecord.deserialize(key) } + + return new SenderKeyRecord() }, - storeSenderKey: async (keyId, key) => { - await keys.set({ 'sender-key': { [keyId]: key.serialize() } }) + storeSenderKey: async (senderKeyName: SenderKeyName, key: SenderKeyRecord) => { + const keyId = senderKeyName.toString() + const serialized = JSON.stringify(key.serialize()) + await keys.set({ 'sender-key': { [keyId]: Buffer.from(serialized, 'utf-8') } }) }, getOurRegistrationId: () => creds.registrationId, getOurIdentity: () => {