@freesignal/protocol 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/crypto.d.ts +1 -55
- package/crypto.js +4 -8
- package/data.d.ts +6 -22
- package/data.js +25 -70
- package/double-ratchet.js +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -2
- package/package.json +1 -1
- package/test.js +3 -2
- package/types.d.ts +67 -0
- package/types.js +38 -1
- package/x3dh.d.ts +2 -3
- package/x3dh.js +8 -8
package/crypto.d.ts
CHANGED
|
@@ -16,61 +16,7 @@
|
|
|
16
16
|
* You should have received a copy of the GNU General Public License
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
|
-
|
|
20
|
-
export type HmacAlgorithms = 'kmac128' | 'kmac256';
|
|
21
|
-
interface UUIDv4 {
|
|
22
|
-
toString(): string;
|
|
23
|
-
toJSON(): string;
|
|
24
|
-
toBuffer(): Uint8Array;
|
|
25
|
-
}
|
|
26
|
-
export interface Crypto {
|
|
27
|
-
hash(message: Uint8Array, algorithm?: HashAlgorithms): Uint8Array;
|
|
28
|
-
hmac(key: Uint8Array, message: Uint8Array, length?: number, algorithm?: HmacAlgorithms): Uint8Array;
|
|
29
|
-
hkdf(key: Uint8Array, salt: Uint8Array, info?: Uint8Array | string, length?: number): Uint8Array;
|
|
30
|
-
readonly KeyPair: typeof Crypto.KeyPair;
|
|
31
|
-
readonly box: Crypto.box;
|
|
32
|
-
readonly ECDH: Crypto.ECDH;
|
|
33
|
-
readonly EdDSA: Crypto.EdDSA;
|
|
34
|
-
readonly UUID: Crypto.UUID;
|
|
35
|
-
randomBytes(n: number): Uint8Array;
|
|
36
|
-
scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array;
|
|
37
|
-
}
|
|
38
|
-
export declare namespace Crypto {
|
|
39
|
-
type KeyPair = {
|
|
40
|
-
readonly publicKey: Uint8Array;
|
|
41
|
-
readonly secretKey: Uint8Array;
|
|
42
|
-
};
|
|
43
|
-
namespace KeyPair {
|
|
44
|
-
function isKeyPair(obj: any): boolean;
|
|
45
|
-
}
|
|
46
|
-
interface box {
|
|
47
|
-
readonly keyLength: number;
|
|
48
|
-
readonly nonceLength: number;
|
|
49
|
-
encrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
50
|
-
decrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | undefined;
|
|
51
|
-
}
|
|
52
|
-
interface ECDH {
|
|
53
|
-
readonly publicKeyLength: number;
|
|
54
|
-
readonly secretKeyLength: number;
|
|
55
|
-
keyPair(secretKey?: Uint8Array): KeyPair;
|
|
56
|
-
sharedKey(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
57
|
-
}
|
|
58
|
-
interface EdDSA {
|
|
59
|
-
readonly publicKeyLength: number;
|
|
60
|
-
readonly secretKeyLength: number;
|
|
61
|
-
readonly signatureLength: number;
|
|
62
|
-
readonly seedLength: number;
|
|
63
|
-
keyPair(secretKey?: Uint8Array): KeyPair;
|
|
64
|
-
keyPairFromSeed(seed: Uint8Array): KeyPair;
|
|
65
|
-
sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
66
|
-
verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
|
|
67
|
-
}
|
|
68
|
-
interface UUID {
|
|
69
|
-
generate(): UUIDv4;
|
|
70
|
-
stringify(arr: Uint8Array, offset?: number): string;
|
|
71
|
-
parse(uuid: string): Uint8Array;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
19
|
+
import { Crypto } from './types';
|
|
74
20
|
declare const crypto: Crypto;
|
|
75
21
|
declare namespace crypto {
|
|
76
22
|
type KeyPair = Crypto.KeyPair;
|
package/crypto.js
CHANGED
|
@@ -21,14 +21,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.Crypto = void 0;
|
|
25
24
|
const js_sha3_1 = require("js-sha3");
|
|
26
25
|
const tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
27
26
|
const uuid_1 = require("uuid");
|
|
28
27
|
const utils_1 = require("./utils");
|
|
29
|
-
var Crypto;
|
|
30
|
-
(function (Crypto) {
|
|
31
|
-
})(Crypto || (exports.Crypto = Crypto = {}));
|
|
32
28
|
class CryptoConstructor {
|
|
33
29
|
constructor() {
|
|
34
30
|
this.KeyPair = {
|
|
@@ -72,9 +68,6 @@ class CryptoConstructor {
|
|
|
72
68
|
hkdf(key, salt, info, length = 32) {
|
|
73
69
|
return new Uint8Array(js_sha3_1.kmac256.digest(key, salt, length * 8, info !== null && info !== void 0 ? info : new Uint8Array()));
|
|
74
70
|
}
|
|
75
|
-
scalarMult(n, p) {
|
|
76
|
-
return tweetnacl_1.default.scalarMult(n, p);
|
|
77
|
-
}
|
|
78
71
|
}
|
|
79
72
|
(function (CryptoConstructor) {
|
|
80
73
|
class box {
|
|
@@ -102,7 +95,10 @@ class CryptoConstructor {
|
|
|
102
95
|
return tweetnacl_1.default.box.keyPair();
|
|
103
96
|
}
|
|
104
97
|
sharedKey(publicKey, secretKey) {
|
|
105
|
-
return tweetnacl_1.default.
|
|
98
|
+
return tweetnacl_1.default.box.before(publicKey, secretKey);
|
|
99
|
+
}
|
|
100
|
+
scalarMult(n, p) {
|
|
101
|
+
return tweetnacl_1.default.scalarMult(n, p);
|
|
106
102
|
}
|
|
107
103
|
}
|
|
108
104
|
CryptoConstructor.ECDH = ECDH;
|
package/data.d.ts
CHANGED
|
@@ -16,26 +16,12 @@
|
|
|
16
16
|
* You should have received a copy of the GNU General Public License
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
|
-
import { Encodable } from "./types";
|
|
20
|
-
export declare enum Protocols {
|
|
21
|
-
NULL = "",
|
|
22
|
-
MESSAGE = "/freesignal/message/1.0.0",
|
|
23
|
-
RELAY = "/freesignal/relay/1.0.0"
|
|
24
|
-
}
|
|
25
|
-
export declare namespace Protocols {
|
|
26
|
-
function isProtocol(protocol: any): boolean;
|
|
27
|
-
function fromCode(code: number): Protocols;
|
|
28
|
-
function toCode(protocol: Protocols): number;
|
|
29
|
-
function encode(protocol: Protocols, length?: number): Uint8Array;
|
|
30
|
-
function decode(array: Uint8Array): Protocols;
|
|
31
|
-
}
|
|
19
|
+
import { Encodable, Protocols } from "./types";
|
|
32
20
|
export interface Datagram {
|
|
33
21
|
readonly id: string;
|
|
34
22
|
readonly version: number;
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
readonly receiverKey: string;
|
|
38
|
-
readonly receiverRelay?: string;
|
|
23
|
+
readonly sender: string;
|
|
24
|
+
readonly receiver: string;
|
|
39
25
|
readonly protocol: Protocols;
|
|
40
26
|
readonly createdAt: number;
|
|
41
27
|
payload?: Uint8Array;
|
|
@@ -44,15 +30,13 @@ export declare namespace Datagram {
|
|
|
44
30
|
const version = 1;
|
|
45
31
|
function create(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable): DatagramConstructor;
|
|
46
32
|
function isDatagram(obj: any): boolean;
|
|
47
|
-
function from(data: Uint8Array): DatagramConstructor;
|
|
33
|
+
function from(data: Uint8Array | Datagram | string): DatagramConstructor;
|
|
48
34
|
}
|
|
49
35
|
declare class DatagramConstructor implements Encodable, Datagram {
|
|
50
36
|
readonly id: string;
|
|
51
37
|
readonly version: number;
|
|
52
|
-
readonly
|
|
53
|
-
readonly
|
|
54
|
-
readonly receiverKey: string;
|
|
55
|
-
readonly receiverRelay?: string;
|
|
38
|
+
readonly sender: string;
|
|
39
|
+
readonly receiver: string;
|
|
56
40
|
readonly protocol: Protocols;
|
|
57
41
|
readonly createdAt: number;
|
|
58
42
|
payload?: Uint8Array;
|
package/data.js
CHANGED
|
@@ -21,43 +21,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.Datagram =
|
|
24
|
+
exports.Datagram = void 0;
|
|
25
25
|
const utils_1 = require("./utils");
|
|
26
26
|
const crypto_1 = __importDefault(require("./crypto"));
|
|
27
27
|
const fflate_1 = __importDefault(require("fflate"));
|
|
28
|
-
|
|
29
|
-
(function (Protocols) {
|
|
30
|
-
Protocols["NULL"] = "";
|
|
31
|
-
Protocols["MESSAGE"] = "/freesignal/message/1.0.0";
|
|
32
|
-
Protocols["RELAY"] = "/freesignal/relay/1.0.0";
|
|
33
|
-
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
34
|
-
(function (Protocols) {
|
|
35
|
-
function isProtocol(protocol) {
|
|
36
|
-
return Object.values(Protocols).includes(protocol);
|
|
37
|
-
}
|
|
38
|
-
Protocols.isProtocol = isProtocol;
|
|
39
|
-
function fromCode(code) {
|
|
40
|
-
return Object.values(Protocols)[code];
|
|
41
|
-
}
|
|
42
|
-
Protocols.fromCode = fromCode;
|
|
43
|
-
function toCode(protocol) {
|
|
44
|
-
return Object.values(Protocols).indexOf(protocol);
|
|
45
|
-
}
|
|
46
|
-
Protocols.toCode = toCode;
|
|
47
|
-
function encode(protocol, length) {
|
|
48
|
-
/*const raw = numberToUint8Array(Protocols.toCode(protocol), length).reverse();
|
|
49
|
-
raw[0] |= (raw.length - 1) << 6;
|
|
50
|
-
return raw;*/
|
|
51
|
-
return (0, utils_1.numberToUint8Array)(Protocols.toCode(protocol), length !== null && length !== void 0 ? length : 4, 'big');
|
|
52
|
-
}
|
|
53
|
-
Protocols.encode = encode;
|
|
54
|
-
function decode(array) {
|
|
55
|
-
array[0] &= 63;
|
|
56
|
-
array = array.reverse();
|
|
57
|
-
return Protocols.fromCode((0, utils_1.numberFromUint8Array)(array));
|
|
58
|
-
}
|
|
59
|
-
Protocols.decode = decode;
|
|
60
|
-
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
28
|
+
const types_1 = require("./types");
|
|
61
29
|
var Datagram;
|
|
62
30
|
(function (Datagram) {
|
|
63
31
|
Datagram.version = 1;
|
|
@@ -70,7 +38,12 @@ var Datagram;
|
|
|
70
38
|
}
|
|
71
39
|
Datagram.isDatagram = isDatagram;
|
|
72
40
|
function from(data) {
|
|
73
|
-
|
|
41
|
+
if (typeof data === 'string') {
|
|
42
|
+
const decoded = (0, utils_1.decodeBase64)(data);
|
|
43
|
+
return new DatagramConstructor(decoded);
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
return new DatagramConstructor(data);
|
|
74
47
|
}
|
|
75
48
|
Datagram.from = from;
|
|
76
49
|
})(Datagram || (exports.Datagram = Datagram = {}));
|
|
@@ -79,35 +52,29 @@ class DatagramConstructor {
|
|
|
79
52
|
if (!receiver && !protocol && !payload) {
|
|
80
53
|
if (data instanceof Uint8Array) {
|
|
81
54
|
this.version = data[0] & 63;
|
|
82
|
-
this.protocol = Protocols.decode(data.subarray(1,
|
|
83
|
-
this.id = crypto_1.default.UUID.stringify(data.subarray(
|
|
84
|
-
this.createdAt = (0, utils_1.numberFromUint8Array)(data.subarray(
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
const senderRelayOffset = data.indexOf(255, DatagramConstructor.headerOffset);
|
|
88
|
-
const receiverRelayOffset = data.indexOf(255, senderRelayOffset + 1);
|
|
89
|
-
this.senderRelay = (0, utils_1.encodeUTF8)(data.subarray(DatagramConstructor.headerOffset, senderRelayOffset)) ? "" : undefined;
|
|
90
|
-
this.receiverRelay = (0, utils_1.encodeUTF8)(data.subarray(senderRelayOffset + 1, receiverRelayOffset)) ? "" : undefined;
|
|
55
|
+
this.protocol = types_1.Protocols.decode(data.subarray(1, 2));
|
|
56
|
+
this.id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
|
|
57
|
+
this.createdAt = (0, utils_1.numberFromUint8Array)(data.subarray(18, 26));
|
|
58
|
+
this.sender = (0, utils_1.encodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength));
|
|
59
|
+
this.receiver = (0, utils_1.encodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramConstructor.headerOffset));
|
|
91
60
|
if (data[0] & 128) {
|
|
92
61
|
const signature = data.subarray(data.length - crypto_1.default.EdDSA.signatureLength);
|
|
93
|
-
if (!crypto_1.default.EdDSA.verify(data.subarray(0, data.length - crypto_1.default.EdDSA.signatureLength), signature, data.subarray(
|
|
62
|
+
if (!crypto_1.default.EdDSA.verify(data.subarray(0, data.length - crypto_1.default.EdDSA.signatureLength), signature, data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength)))
|
|
94
63
|
throw new Error('Invalid signature for Datagram');
|
|
95
64
|
}
|
|
96
65
|
if (data[0] & 64)
|
|
97
|
-
this.payload = fflate_1.default.inflateSync(data.subarray(
|
|
66
|
+
this.payload = fflate_1.default.inflateSync(data.subarray(DatagramConstructor.headerOffset, data.length));
|
|
98
67
|
else
|
|
99
|
-
this.payload = data.subarray(
|
|
68
|
+
this.payload = data.subarray(DatagramConstructor.headerOffset, data.length);
|
|
100
69
|
}
|
|
101
70
|
else if (Datagram.isDatagram(data)) {
|
|
102
71
|
const datagram = data;
|
|
103
72
|
this.id = datagram.id;
|
|
104
73
|
this.version = datagram.version;
|
|
105
|
-
this.
|
|
106
|
-
this.
|
|
74
|
+
this.sender = datagram.sender;
|
|
75
|
+
this.receiver = datagram.receiver;
|
|
107
76
|
this.protocol = datagram.protocol;
|
|
108
77
|
this.createdAt = datagram.createdAt;
|
|
109
|
-
this.senderRelay = datagram.senderRelay;
|
|
110
|
-
this.receiverRelay = datagram.receiverRelay;
|
|
111
78
|
this.payload = datagram.payload;
|
|
112
79
|
}
|
|
113
80
|
else
|
|
@@ -116,20 +83,8 @@ class DatagramConstructor {
|
|
|
116
83
|
else if (typeof data === 'string' || data instanceof Uint8Array) {
|
|
117
84
|
this.id = crypto_1.default.UUID.generate().toString();
|
|
118
85
|
this.version = Datagram.version;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.senderKey = address[0];
|
|
122
|
-
this.senderRelay = address[1];
|
|
123
|
-
}
|
|
124
|
-
else
|
|
125
|
-
this.senderKey = (0, utils_1.encodeBase64)(data);
|
|
126
|
-
if (typeof receiver === 'string') {
|
|
127
|
-
const address = receiver.split('@');
|
|
128
|
-
this.receiverKey = address[0];
|
|
129
|
-
this.receiverRelay = address[1];
|
|
130
|
-
}
|
|
131
|
-
else
|
|
132
|
-
this.receiverKey = (0, utils_1.encodeBase64)(receiver);
|
|
86
|
+
this.sender = typeof data === 'string' ? data : (0, utils_1.encodeBase64)(data);
|
|
87
|
+
this.receiver = typeof receiver === 'string' ? receiver : (0, utils_1.encodeBase64)(receiver);
|
|
133
88
|
this.protocol = protocol;
|
|
134
89
|
this.createdAt = Date.now();
|
|
135
90
|
this.payload = payload instanceof Uint8Array ? payload : payload === null || payload === void 0 ? void 0 : payload.encode();
|
|
@@ -141,12 +96,12 @@ class DatagramConstructor {
|
|
|
141
96
|
var _a;
|
|
142
97
|
compression = compression && this.payload != undefined && this.payload.length > 1024;
|
|
143
98
|
return (0, utils_1.concatUint8Array)(new Uint8Array(1).fill(this.version | (compression ? 64 : 0)), //1
|
|
144
|
-
Protocols.encode(this.protocol
|
|
99
|
+
types_1.Protocols.encode(this.protocol), //1
|
|
145
100
|
(_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
|
|
146
101
|
(0, utils_1.numberToUint8Array)(this.createdAt, 8), //8
|
|
147
|
-
(0, utils_1.decodeBase64)(this.
|
|
148
|
-
(0, utils_1.decodeBase64)(this.
|
|
149
|
-
...(this.
|
|
102
|
+
(0, utils_1.decodeBase64)(this.sender), //32
|
|
103
|
+
(0, utils_1.decodeBase64)(this.receiver), //32
|
|
104
|
+
...(this.payload ? [compression ? fflate_1.default.deflateSync(this.payload) : this.payload] : []));
|
|
150
105
|
}
|
|
151
106
|
encodeSigned(secretKey, compression) {
|
|
152
107
|
//if (!this.payload) throw new Error('Cannot sign a datagram without payload');
|
|
@@ -173,4 +128,4 @@ class DatagramConstructor {
|
|
|
173
128
|
return this.toString();
|
|
174
129
|
}
|
|
175
130
|
}
|
|
176
|
-
DatagramConstructor.headerOffset =
|
|
131
|
+
DatagramConstructor.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
|
package/double-ratchet.js
CHANGED
|
@@ -69,7 +69,7 @@ class KeySession {
|
|
|
69
69
|
ratchetKeys(info) {
|
|
70
70
|
if (!this._remoteKey)
|
|
71
71
|
throw new Error();
|
|
72
|
-
const sharedKey = crypto_1.default.scalarMult(this.keyPair.secretKey, this._remoteKey);
|
|
72
|
+
const sharedKey = crypto_1.default.ECDH.scalarMult(this.keyPair.secretKey, this._remoteKey);
|
|
73
73
|
if (!this.rootKey)
|
|
74
74
|
this.rootKey = crypto_1.default.hash(sharedKey);
|
|
75
75
|
const hashkey = crypto_1.default.hkdf(sharedKey, this.rootKey, info, KeySession.keyLength * 2);
|
package/index.d.ts
CHANGED
|
@@ -42,5 +42,5 @@ export declare function createKeySession(opts?: {
|
|
|
42
42
|
*/
|
|
43
43
|
export declare function createKeyExchange(signSecretKey: Uint8Array, boxSecretKey: Uint8Array, bundleStore?: LocalStorage<string, crypto.KeyPair>): KeyExchange;
|
|
44
44
|
export * from "./types";
|
|
45
|
-
export {
|
|
45
|
+
export { Datagram } from "./data";
|
|
46
46
|
export { EncryptedData } from "./double-ratchet";
|
package/index.js
CHANGED
|
@@ -32,7 +32,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
32
32
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.EncryptedData = exports.Datagram =
|
|
35
|
+
exports.EncryptedData = exports.Datagram = void 0;
|
|
36
36
|
exports.createKeySession = createKeySession;
|
|
37
37
|
exports.createKeyExchange = createKeyExchange;
|
|
38
38
|
const double_ratchet_1 = require("./double-ratchet");
|
|
@@ -60,7 +60,6 @@ function createKeyExchange(signSecretKey, boxSecretKey, bundleStore) {
|
|
|
60
60
|
}
|
|
61
61
|
__exportStar(require("./types"), exports);
|
|
62
62
|
var data_1 = require("./data");
|
|
63
|
-
Object.defineProperty(exports, "Protocols", { enumerable: true, get: function () { return data_1.Protocols; } });
|
|
64
63
|
Object.defineProperty(exports, "Datagram", { enumerable: true, get: function () { return data_1.Datagram; } });
|
|
65
64
|
var double_ratchet_2 = require("./double-ratchet");
|
|
66
65
|
Object.defineProperty(exports, "EncryptedData", { enumerable: true, get: function () { return double_ratchet_2.EncryptedData; } });
|
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -7,6 +7,7 @@ const _1 = require(".");
|
|
|
7
7
|
const crypto_1 = __importDefault(require("./crypto"));
|
|
8
8
|
const data_1 = require("./data");
|
|
9
9
|
const utils_1 = require("./utils");
|
|
10
|
+
const types_1 = require("./types");
|
|
10
11
|
const bob = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair().secretKey, crypto_1.default.ECDH.keyPair().secretKey);
|
|
11
12
|
const alice = (0, _1.createKeyExchange)(crypto_1.default.EdDSA.keyPair().secretKey, crypto_1.default.ECDH.keyPair().secretKey);
|
|
12
13
|
const bobmessage = bob.generateData();
|
|
@@ -15,7 +16,7 @@ bob.digestMessage(aliceack).then(({ session: bobsession, cleartext }) => {
|
|
|
15
16
|
var _a;
|
|
16
17
|
if (bobsession && cleartext) {
|
|
17
18
|
console.log("Session established successfully between Alice and Bob.");
|
|
18
|
-
const datagram = data_1.Datagram.create(bob.signatureKey, alice.signatureKey,
|
|
19
|
+
const datagram = data_1.Datagram.create(bob.signatureKey, alice.signatureKey, types_1.Protocols.MESSAGE, (_a = bobsession.encrypt((0, utils_1.decodeUTF8)("Hi Alice!"))) === null || _a === void 0 ? void 0 : _a.encode());
|
|
19
20
|
//console.log(datagram.payload);
|
|
20
21
|
const msg = datagram.encode();
|
|
21
22
|
console.log((0, utils_1.encodeUTF8)(alicesession.decrypt(data_1.Datagram.from(msg).payload)));
|
|
@@ -23,7 +24,7 @@ bob.digestMessage(aliceack).then(({ session: bobsession, cleartext }) => {
|
|
|
23
24
|
console.log("Successfully handshaked");
|
|
24
25
|
else
|
|
25
26
|
console.log("Error during handshake");
|
|
26
|
-
const longmsg = data_1.Datagram.create(alice.signatureKey, bob.signatureKey,
|
|
27
|
+
const longmsg = data_1.Datagram.create(alice.signatureKey, bob.signatureKey, types_1.Protocols.MESSAGE, alicesession.encrypt(new Uint8Array(1000000).fill(33).map(val => val + Math.floor(Math.random() * 93))));
|
|
27
28
|
console.log(longmsg.encode().length);
|
|
28
29
|
console.log(longmsg.encode(false).length);
|
|
29
30
|
}
|
package/types.d.ts
CHANGED
|
@@ -25,6 +25,18 @@ export interface Encodable {
|
|
|
25
25
|
export declare namespace Encodable {
|
|
26
26
|
function isEncodable(obj: any): boolean;
|
|
27
27
|
}
|
|
28
|
+
export declare enum Protocols {
|
|
29
|
+
NULL = "",
|
|
30
|
+
MESSAGE = "/freesignal/message/1.0.0",
|
|
31
|
+
RELAY = "/freesignal/relay/1.0.0"
|
|
32
|
+
}
|
|
33
|
+
export declare namespace Protocols {
|
|
34
|
+
function isProtocol(protocol: any): boolean;
|
|
35
|
+
function fromCode(code: number): Protocols;
|
|
36
|
+
function toCode(protocol: Protocols): number;
|
|
37
|
+
function encode(protocol: Protocols, length?: number): Uint8Array;
|
|
38
|
+
function decode(array: Uint8Array): Protocols;
|
|
39
|
+
}
|
|
28
40
|
type LocalStorageIterator<T> = Iterable<T>;
|
|
29
41
|
export interface LocalStorage<K, T> {
|
|
30
42
|
set(key: K, value: T): Promise<this>;
|
|
@@ -58,4 +70,59 @@ export interface KeyExchangeDataBundle {
|
|
|
58
70
|
readonly signature: string;
|
|
59
71
|
readonly onetimePreKey: string[];
|
|
60
72
|
}
|
|
73
|
+
interface UUIDv4 {
|
|
74
|
+
toString(): string;
|
|
75
|
+
toJSON(): string;
|
|
76
|
+
toBuffer(): Uint8Array;
|
|
77
|
+
}
|
|
78
|
+
export interface Crypto {
|
|
79
|
+
hash(message: Uint8Array, algorithm?: Crypto.HashAlgorithms): Uint8Array;
|
|
80
|
+
hmac(key: Uint8Array, message: Uint8Array, length?: number, algorithm?: Crypto.HmacAlgorithms): Uint8Array;
|
|
81
|
+
hkdf(key: Uint8Array, salt: Uint8Array, info?: Uint8Array | string, length?: number): Uint8Array;
|
|
82
|
+
readonly KeyPair: typeof Crypto.KeyPair;
|
|
83
|
+
readonly box: Crypto.box;
|
|
84
|
+
readonly ECDH: Crypto.ECDH;
|
|
85
|
+
readonly EdDSA: Crypto.EdDSA;
|
|
86
|
+
readonly UUID: Crypto.UUID;
|
|
87
|
+
randomBytes(n: number): Uint8Array;
|
|
88
|
+
}
|
|
89
|
+
export declare namespace Crypto {
|
|
90
|
+
type HashAlgorithms = 'sha224' | 'sha256' | 'sha384' | 'sha512';
|
|
91
|
+
type HmacAlgorithms = 'kmac128' | 'kmac256';
|
|
92
|
+
type KeyPair = {
|
|
93
|
+
readonly publicKey: Uint8Array;
|
|
94
|
+
readonly secretKey: Uint8Array;
|
|
95
|
+
};
|
|
96
|
+
namespace KeyPair {
|
|
97
|
+
function isKeyPair(obj: any): boolean;
|
|
98
|
+
}
|
|
99
|
+
interface box {
|
|
100
|
+
readonly keyLength: number;
|
|
101
|
+
readonly nonceLength: number;
|
|
102
|
+
encrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
103
|
+
decrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | undefined;
|
|
104
|
+
}
|
|
105
|
+
interface ECDH {
|
|
106
|
+
readonly publicKeyLength: number;
|
|
107
|
+
readonly secretKeyLength: number;
|
|
108
|
+
keyPair(secretKey?: Uint8Array): KeyPair;
|
|
109
|
+
sharedKey(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
110
|
+
scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array;
|
|
111
|
+
}
|
|
112
|
+
interface EdDSA {
|
|
113
|
+
readonly publicKeyLength: number;
|
|
114
|
+
readonly secretKeyLength: number;
|
|
115
|
+
readonly signatureLength: number;
|
|
116
|
+
readonly seedLength: number;
|
|
117
|
+
keyPair(secretKey?: Uint8Array): KeyPair;
|
|
118
|
+
keyPairFromSeed(seed: Uint8Array): KeyPair;
|
|
119
|
+
sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
120
|
+
verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
|
|
121
|
+
}
|
|
122
|
+
interface UUID {
|
|
123
|
+
generate(): UUIDv4;
|
|
124
|
+
stringify(arr: Uint8Array, offset?: number): string;
|
|
125
|
+
parse(uuid: string): Uint8Array;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
61
128
|
export {};
|
package/types.js
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.Encodable = void 0;
|
|
21
|
+
exports.Crypto = exports.Protocols = exports.Encodable = void 0;
|
|
22
|
+
const utils_1 = require("./utils");
|
|
22
23
|
var Encodable;
|
|
23
24
|
(function (Encodable) {
|
|
24
25
|
const properties = ['encode', 'toString', 'toJSON'];
|
|
@@ -27,3 +28,39 @@ var Encodable;
|
|
|
27
28
|
}
|
|
28
29
|
Encodable.isEncodable = isEncodable;
|
|
29
30
|
})(Encodable || (exports.Encodable = Encodable = {}));
|
|
31
|
+
var Protocols;
|
|
32
|
+
(function (Protocols) {
|
|
33
|
+
Protocols["NULL"] = "";
|
|
34
|
+
Protocols["MESSAGE"] = "/freesignal/message/1.0.0";
|
|
35
|
+
Protocols["RELAY"] = "/freesignal/relay/1.0.0";
|
|
36
|
+
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
37
|
+
(function (Protocols) {
|
|
38
|
+
function isProtocol(protocol) {
|
|
39
|
+
return Object.values(Protocols).includes(protocol);
|
|
40
|
+
}
|
|
41
|
+
Protocols.isProtocol = isProtocol;
|
|
42
|
+
function fromCode(code) {
|
|
43
|
+
return Object.values(Protocols)[code];
|
|
44
|
+
}
|
|
45
|
+
Protocols.fromCode = fromCode;
|
|
46
|
+
function toCode(protocol) {
|
|
47
|
+
return Object.values(Protocols).indexOf(protocol);
|
|
48
|
+
}
|
|
49
|
+
Protocols.toCode = toCode;
|
|
50
|
+
function encode(protocol, length) {
|
|
51
|
+
/*const raw = numberToUint8Array(Protocols.toCode(protocol), length).reverse();
|
|
52
|
+
raw[0] |= (raw.length - 1) << 6;
|
|
53
|
+
return raw;*/
|
|
54
|
+
return (0, utils_1.numberToUint8Array)(Protocols.toCode(protocol), length);
|
|
55
|
+
}
|
|
56
|
+
Protocols.encode = encode;
|
|
57
|
+
function decode(array) {
|
|
58
|
+
//array[0] &= 63;
|
|
59
|
+
//array = array.reverse();
|
|
60
|
+
return Protocols.fromCode((0, utils_1.numberFromUint8Array)(array));
|
|
61
|
+
}
|
|
62
|
+
Protocols.decode = decode;
|
|
63
|
+
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
64
|
+
var Crypto;
|
|
65
|
+
(function (Crypto) {
|
|
66
|
+
})(Crypto || (exports.Crypto = Crypto = {}));
|
package/x3dh.d.ts
CHANGED
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
* You should have received a copy of the GNU General Public License
|
|
17
17
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
18
18
|
*/
|
|
19
|
-
import
|
|
20
|
-
import { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage } from "./types";
|
|
19
|
+
import { KeyExchangeData, KeyExchangeDataBundle, KeyExchangeSynMessage, LocalStorage, Crypto } from "./types";
|
|
21
20
|
import { KeySession } from "./double-ratchet";
|
|
22
21
|
export declare class KeyExchange {
|
|
23
22
|
static readonly version = 1;
|
|
@@ -26,7 +25,7 @@ export declare class KeyExchange {
|
|
|
26
25
|
private readonly _signatureKey;
|
|
27
26
|
private readonly _identityKey;
|
|
28
27
|
private readonly bundleStore;
|
|
29
|
-
constructor(signSecretKey: Uint8Array, boxSecretKey: Uint8Array, bundleStore?: LocalStorage<string,
|
|
28
|
+
constructor(signSecretKey: Uint8Array, boxSecretKey: Uint8Array, bundleStore?: LocalStorage<string, Crypto.KeyPair>);
|
|
30
29
|
get signatureKey(): Uint8Array;
|
|
31
30
|
get identityKey(): Uint8Array;
|
|
32
31
|
private generateSPK;
|
package/x3dh.js
CHANGED
|
@@ -88,10 +88,10 @@ class KeyExchange {
|
|
|
88
88
|
const signedPreKeyHash = crypto_1.default.hash(signedPreKey);
|
|
89
89
|
const onetimePreKeyHash = onetimePreKey ? crypto_1.default.hash(onetimePreKey) : new Uint8Array();
|
|
90
90
|
const rootKey = crypto_1.default.hkdf(new Uint8Array([
|
|
91
|
-
...crypto_1.default.scalarMult(this._identityKey.secretKey, signedPreKey),
|
|
92
|
-
...crypto_1.default.scalarMult(ephemeralKey.secretKey, identityKey),
|
|
93
|
-
...crypto_1.default.scalarMult(ephemeralKey.secretKey, signedPreKey),
|
|
94
|
-
...onetimePreKey ? crypto_1.default.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
91
|
+
...crypto_1.default.ECDH.scalarMult(this._identityKey.secretKey, signedPreKey),
|
|
92
|
+
...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, identityKey),
|
|
93
|
+
...crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, signedPreKey),
|
|
94
|
+
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(ephemeralKey.secretKey, onetimePreKey) : new Uint8Array()
|
|
95
95
|
]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
|
|
96
96
|
const session = new double_ratchet_1.KeySession({ remoteKey: identityKey, rootKey });
|
|
97
97
|
const cyphertext = session.encrypt((0, utils_1.concatUint8Array)(crypto_1.default.hash(this._identityKey.publicKey), crypto_1.default.hash(identityKey)));
|
|
@@ -122,10 +122,10 @@ class KeyExchange {
|
|
|
122
122
|
const identityKey = (0, utils_1.decodeBase64)(message.identityKey);
|
|
123
123
|
const ephemeralKey = (0, utils_1.decodeBase64)(message.ephemeralKey);
|
|
124
124
|
const rootKey = crypto_1.default.hkdf(new Uint8Array([
|
|
125
|
-
...crypto_1.default.scalarMult(signedPreKey.secretKey, identityKey),
|
|
126
|
-
...crypto_1.default.scalarMult(this._identityKey.secretKey, ephemeralKey),
|
|
127
|
-
...crypto_1.default.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
128
|
-
...onetimePreKey ? crypto_1.default.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
125
|
+
...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, identityKey),
|
|
126
|
+
...crypto_1.default.ECDH.scalarMult(this._identityKey.secretKey, ephemeralKey),
|
|
127
|
+
...crypto_1.default.ECDH.scalarMult(signedPreKey.secretKey, ephemeralKey),
|
|
128
|
+
...onetimePreKey ? crypto_1.default.ECDH.scalarMult(onetimePreKey.secretKey, ephemeralKey) : new Uint8Array()
|
|
129
129
|
]), new Uint8Array(double_ratchet_1.KeySession.rootKeyLength).fill(0), KeyExchange.hkdfInfo, double_ratchet_1.KeySession.rootKeyLength);
|
|
130
130
|
const session = new double_ratchet_1.KeySession({ secretKey: this._identityKey.secretKey, rootKey });
|
|
131
131
|
const cleartext = session.decrypt((0, utils_1.decodeBase64)(message.associatedData));
|