@freesignal/protocol 0.1.0

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 ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * FreeSignal Protocol
3
+ *
4
+ * Copyright (C) 2025 Christian Braghette
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
18
+ */
19
+ export type HashAlgorithms = 'sha224' | 'sha256' | 'sha384' | 'sha512';
20
+ export type HmacAlgorithms = 'kmac128' | 'kmac256';
21
+ export interface Crypto {
22
+ hash(message: Uint8Array, algorithm?: HashAlgorithms): Uint8Array;
23
+ hmac(key: Uint8Array, message: Uint8Array, length?: number, algorithm?: HmacAlgorithms): Uint8Array;
24
+ hkdf(key: Uint8Array, salt: Uint8Array, info?: Uint8Array | string, length?: number): Uint8Array;
25
+ readonly box: Crypto.box;
26
+ readonly ECDH: Crypto.ECDH;
27
+ readonly EdDSA: Crypto.EdDSA;
28
+ randomBytes(n: number): Uint8Array;
29
+ scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array;
30
+ generateId(): Crypto.UUID;
31
+ }
32
+ export declare namespace Crypto {
33
+ type KeyPair = {
34
+ publicKey: Uint8Array;
35
+ secretKey: Uint8Array;
36
+ };
37
+ interface box {
38
+ readonly keyLength: number;
39
+ readonly nonceLength: number;
40
+ encrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
41
+ decrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | undefined;
42
+ }
43
+ interface ECDH {
44
+ readonly publicKeyLength: number;
45
+ readonly secretKeyLength: number;
46
+ keyPair(secretKey?: Uint8Array): KeyPair;
47
+ sharedKey(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
48
+ }
49
+ interface EdDSA {
50
+ readonly publicKeyLength: number;
51
+ readonly secretKeyLength: number;
52
+ readonly signatureLength: number;
53
+ readonly seedLength: number;
54
+ keyPair(secretKey?: Uint8Array): KeyPair;
55
+ keyPairFromSeed(seed: Uint8Array): KeyPair;
56
+ sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
57
+ verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
58
+ }
59
+ interface UUID {
60
+ toString(): string;
61
+ toJSON(): string;
62
+ toBuffer(): Uint8Array;
63
+ }
64
+ }
65
+ declare const crypto: Crypto;
66
+ declare namespace crypto {
67
+ type KeyPair = Crypto.KeyPair;
68
+ }
69
+ export default crypto;
package/crypto.js ADDED
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * FreeSignal Protocol
4
+ *
5
+ * Copyright (C) 2025 Christian Braghette
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
19
+ */
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ const js_sha3_1 = require("js-sha3");
25
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
26
+ const uuid_1 = require("uuid");
27
+ const utils_1 = require("./utils");
28
+ class CryptoConstructor {
29
+ constructor() {
30
+ this.box = new CryptoConstructor.box();
31
+ this.ECDH = new CryptoConstructor.ECDH();
32
+ this.EdDSA = new CryptoConstructor.EdDSA();
33
+ this.randomBytes = tweetnacl_1.default.randomBytes;
34
+ }
35
+ hash(message, algorithm = 'sha256') {
36
+ switch (algorithm) {
37
+ case 'sha224':
38
+ return new Uint8Array(js_sha3_1.sha3_224.digest(message));
39
+ case 'sha256':
40
+ return new Uint8Array(js_sha3_1.sha3_256.digest(message));
41
+ case 'sha384':
42
+ return new Uint8Array(js_sha3_1.sha3_384.digest(message));
43
+ case 'sha512':
44
+ return new Uint8Array(js_sha3_1.sha3_512.digest(message));
45
+ default:
46
+ throw new Error("Error hashing");
47
+ }
48
+ }
49
+ hmac(key, message, length = 32, algorithm = 'kmac256') {
50
+ length *= 8;
51
+ switch (algorithm) {
52
+ case 'kmac128':
53
+ return new Uint8Array(js_sha3_1.kmac128.digest(key, message, length, new Uint8Array()));
54
+ case 'kmac256':
55
+ return new Uint8Array(js_sha3_1.kmac256.digest(key, message, length, new Uint8Array()));
56
+ default:
57
+ throw new Error("Error hashing");
58
+ }
59
+ }
60
+ hkdf(key, salt, info, length = 32) {
61
+ return new Uint8Array(js_sha3_1.kmac256.digest(key, salt, length * 8, info !== null && info !== void 0 ? info : new Uint8Array()));
62
+ }
63
+ scalarMult(n, p) {
64
+ return tweetnacl_1.default.scalarMult(n, p);
65
+ }
66
+ generateId() {
67
+ return new CryptoConstructor.UUID();
68
+ }
69
+ }
70
+ (function (CryptoConstructor) {
71
+ class box {
72
+ constructor() {
73
+ this.keyLength = tweetnacl_1.default.secretbox.keyLength;
74
+ this.nonceLength = tweetnacl_1.default.secretbox.nonceLength;
75
+ }
76
+ encrypt(msg, nonce, key) {
77
+ return tweetnacl_1.default.secretbox(msg, nonce, key);
78
+ }
79
+ decrypt(msg, nonce, key) {
80
+ var _a;
81
+ return (_a = tweetnacl_1.default.secretbox.open(msg, nonce, key)) !== null && _a !== void 0 ? _a : undefined;
82
+ }
83
+ }
84
+ CryptoConstructor.box = box;
85
+ class ECDH {
86
+ constructor() {
87
+ this.publicKeyLength = tweetnacl_1.default.box.publicKeyLength;
88
+ this.secretKeyLength = tweetnacl_1.default.box.secretKeyLength;
89
+ }
90
+ keyPair(secretKey) {
91
+ if (secretKey)
92
+ return tweetnacl_1.default.box.keyPair.fromSecretKey(secretKey);
93
+ return tweetnacl_1.default.box.keyPair();
94
+ }
95
+ sharedKey(publicKey, secretKey) {
96
+ return tweetnacl_1.default.scalarMult(publicKey, secretKey);
97
+ }
98
+ }
99
+ CryptoConstructor.ECDH = ECDH;
100
+ class EdDSA {
101
+ constructor() {
102
+ this.publicKeyLength = tweetnacl_1.default.sign.publicKeyLength;
103
+ this.secretKeyLength = tweetnacl_1.default.sign.secretKeyLength;
104
+ this.signatureLength = tweetnacl_1.default.sign.signatureLength;
105
+ this.seedLength = tweetnacl_1.default.sign.seedLength;
106
+ }
107
+ keyPair(secretKey) {
108
+ if (secretKey)
109
+ return tweetnacl_1.default.sign.keyPair.fromSecretKey(secretKey);
110
+ return tweetnacl_1.default.sign.keyPair();
111
+ }
112
+ keyPairFromSeed(seed) {
113
+ return tweetnacl_1.default.sign.keyPair.fromSeed(seed);
114
+ }
115
+ sign(msg, secretKey) {
116
+ return tweetnacl_1.default.sign.detached(msg, secretKey);
117
+ }
118
+ verify(msg, sig, publicKey) {
119
+ return tweetnacl_1.default.sign.detached.verify(msg, sig, publicKey);
120
+ }
121
+ }
122
+ CryptoConstructor.EdDSA = EdDSA;
123
+ class UUID {
124
+ constructor() {
125
+ this.value = (0, uuid_1.v4)();
126
+ }
127
+ toString() {
128
+ return this.value;
129
+ }
130
+ toJSON() {
131
+ return this.value;
132
+ }
133
+ toBuffer() {
134
+ return (0, utils_1.decodeUTF8)(this.value);
135
+ }
136
+ }
137
+ CryptoConstructor.UUID = UUID;
138
+ })(CryptoConstructor || (CryptoConstructor = {}));
139
+ const crypto = new CryptoConstructor();
140
+ exports.default = crypto;
package/data.d.ts ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * FreeSignal Protocol
3
+ *
4
+ * Copyright (C) 2025 Christian Braghette
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
18
+ */
19
+ export interface Encodable {
20
+ readonly length: number;
21
+ encode(): Uint8Array;
22
+ toString(): string;
23
+ toJSON(): string;
24
+ }
25
+ export declare namespace Encodable {
26
+ function isEncodable(obj: any): boolean;
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
+ }
40
+ export interface Datagram {
41
+ readonly id: string;
42
+ readonly version: number;
43
+ readonly sender: string;
44
+ readonly receiver: string;
45
+ readonly protocol: Protocols;
46
+ payload?: Uint8Array;
47
+ readonly createdAt: number;
48
+ }
49
+ export declare namespace Datagram {
50
+ const version = 1;
51
+ function create(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable): DatagramConstructor;
52
+ function isDatagram(obj: any): boolean;
53
+ function from(data: Uint8Array): DatagramConstructor;
54
+ }
55
+ declare class DatagramConstructor implements Encodable, Datagram {
56
+ readonly createdAt: number;
57
+ readonly id: string;
58
+ readonly version: number;
59
+ readonly sender: string;
60
+ readonly receiver: string;
61
+ readonly protocol: Protocols;
62
+ payload?: Uint8Array;
63
+ constructor(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable);
64
+ constructor(data: Uint8Array | Datagram);
65
+ get length(): number;
66
+ encode(): Uint8Array;
67
+ toString(): string;
68
+ toJSON(): string;
69
+ }
70
+ type Attachment = any;
71
+ export interface Message {
72
+ readonly version: number;
73
+ text: string;
74
+ group?: string;
75
+ attachments?: Attachment[];
76
+ }
77
+ export declare namespace Message {
78
+ function isMessage(obj: any): boolean;
79
+ function create(opts?: {
80
+ text?: string;
81
+ group?: string;
82
+ attachments?: Attachment[];
83
+ }): MessageConstructor;
84
+ function from(data: Uint8Array | Message): MessageConstructor;
85
+ }
86
+ declare class MessageConstructor implements Encodable, Message {
87
+ static readonly version = 1;
88
+ readonly version: number;
89
+ text: string;
90
+ group?: string;
91
+ attachments?: Attachment[];
92
+ constructor(opts?: {
93
+ text?: string;
94
+ group?: string;
95
+ attachments?: Attachment[];
96
+ });
97
+ constructor(data: Uint8Array | Message);
98
+ get length(): number;
99
+ setText(str: string): this;
100
+ setGroup(str: string): this;
101
+ addAttachment(obj: Attachment): this;
102
+ delAttachment(obj: Attachment): this | undefined;
103
+ encode(): Uint8Array;
104
+ toString(): string;
105
+ toJSON(): string;
106
+ }
107
+ export {};
package/data.js ADDED
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ /**
3
+ * FreeSignal Protocol
4
+ *
5
+ * Copyright (C) 2025 Christian Braghette
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
19
+ */
20
+ var __importDefault = (this && this.__importDefault) || function (mod) {
21
+ return (mod && mod.__esModule) ? mod : { "default": mod };
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.Message = exports.Datagram = exports.Protocols = exports.Encodable = void 0;
25
+ const utils_1 = require("./utils");
26
+ const crypto_1 = __importDefault(require("./crypto"));
27
+ var Encodable;
28
+ (function (Encodable) {
29
+ const properties = ['length', 'encode', 'toString', 'toJSON'];
30
+ function isEncodable(obj) {
31
+ return !properties.some(prop => !obj[prop]);
32
+ }
33
+ Encodable.isEncodable = isEncodable;
34
+ })(Encodable || (exports.Encodable = Encodable = {}));
35
+ var Protocols;
36
+ (function (Protocols) {
37
+ Protocols["NULL"] = "";
38
+ Protocols["MESSAGE"] = "/freesignal/message/1.0.0";
39
+ Protocols["RELAY"] = "/freesignal/relay/1.0.0";
40
+ })(Protocols || (exports.Protocols = Protocols = {}));
41
+ (function (Protocols) {
42
+ function isProtocol(protocol) {
43
+ return Object.values(Protocols).includes(protocol);
44
+ }
45
+ Protocols.isProtocol = isProtocol;
46
+ function fromCode(code) {
47
+ return Object.values(Protocols)[code];
48
+ }
49
+ Protocols.fromCode = fromCode;
50
+ function toCode(protocol) {
51
+ return Object.values(Protocols).indexOf(protocol);
52
+ }
53
+ Protocols.toCode = toCode;
54
+ function encode(protocol, length) {
55
+ const raw = (0, utils_1.numberToUint8Array)(Protocols.toCode(protocol), length).reverse();
56
+ raw[0] |= (raw.length - 1) << 6;
57
+ return raw;
58
+ }
59
+ Protocols.encode = encode;
60
+ function decode(array) {
61
+ array[0] &= 63;
62
+ array = array.reverse();
63
+ return Protocols.fromCode((0, utils_1.numberFromUint8Array)(array));
64
+ }
65
+ Protocols.decode = decode;
66
+ })(Protocols || (exports.Protocols = Protocols = {}));
67
+ var Datagram;
68
+ (function (Datagram) {
69
+ Datagram.version = 1;
70
+ function create(sender, receiver, protocol, payload) {
71
+ return new DatagramConstructor(sender, receiver, protocol, payload);
72
+ }
73
+ Datagram.create = create;
74
+ function isDatagram(obj) {
75
+ return obj instanceof DatagramConstructor || (obj && typeof obj === 'object' && 'id' in obj && 'version' in obj && 'sender' in obj && 'receiver' in obj && 'protocol' in obj && 'createdAt' in obj);
76
+ }
77
+ Datagram.isDatagram = isDatagram;
78
+ function from(data) {
79
+ return new DatagramConstructor(data);
80
+ }
81
+ Datagram.from = from;
82
+ })(Datagram || (exports.Datagram = Datagram = {}));
83
+ class DatagramConstructor {
84
+ constructor(data, receiver, protocol, payload) {
85
+ if (!receiver && !protocol && !payload) {
86
+ if (data instanceof Uint8Array) {
87
+ const obj = (0, utils_1.encodeUTF8)(data).split(',');
88
+ this.id = obj[0];
89
+ this.version = parseInt(obj[1]);
90
+ this.sender = obj[2];
91
+ this.receiver = obj[3];
92
+ this.protocol = Protocols.fromCode(parseInt(obj[4]));
93
+ this.payload = obj[5] ? (0, utils_1.decodeUTF8)(obj[5]) : undefined;
94
+ this.createdAt = parseInt(obj[6]);
95
+ }
96
+ else {
97
+ const datagram = data;
98
+ this.id = datagram.id;
99
+ this.version = datagram.version;
100
+ this.sender = datagram.sender;
101
+ this.receiver = datagram.receiver;
102
+ this.protocol = datagram.protocol;
103
+ this.payload = datagram.payload;
104
+ this.createdAt = datagram.createdAt;
105
+ }
106
+ }
107
+ else if (typeof data === 'string' || data instanceof Uint8Array) {
108
+ this.id = crypto_1.default.generateId().toString();
109
+ this.version = Datagram.version;
110
+ this.sender = typeof data === 'string' ? data : (0, utils_1.encodeBase64)(data);
111
+ this.receiver = typeof receiver === 'string' ? receiver : (0, utils_1.encodeBase64)(receiver);
112
+ this.protocol = protocol;
113
+ this.payload = payload instanceof Uint8Array ? payload : payload === null || payload === void 0 ? void 0 : payload.encode();
114
+ this.createdAt = Date.now();
115
+ }
116
+ else
117
+ throw new Error('Invalid constructor arguments for Datagram');
118
+ }
119
+ get length() { return this.encode().length; }
120
+ encode() {
121
+ return (0, utils_1.decodeUTF8)([
122
+ this.id,
123
+ this.version,
124
+ this.sender,
125
+ this.receiver,
126
+ Protocols.toCode(this.protocol).toString(),
127
+ this.payload ? this.payload : undefined, this.createdAt
128
+ ].filter(x => x !== undefined).join(','));
129
+ }
130
+ toString() {
131
+ return this.toJSON();
132
+ }
133
+ toJSON() {
134
+ return JSON.stringify({
135
+ id: this.id,
136
+ version: this.version,
137
+ sender: this.sender,
138
+ receiver: this.receiver,
139
+ protocol: this.protocol,
140
+ payload: this.payload ? (0, utils_1.encodeUTF8)(this.payload) : undefined,
141
+ createdAt: this.createdAt
142
+ });
143
+ }
144
+ }
145
+ var Message;
146
+ (function (Message) {
147
+ function isMessage(obj) {
148
+ return obj instanceof MessageConstructor || (obj && typeof obj === 'object' && 'version' in obj && 'text' in obj && 'group' in obj && 'attachments' in obj);
149
+ }
150
+ Message.isMessage = isMessage;
151
+ function create(opts) {
152
+ return new MessageConstructor(opts);
153
+ }
154
+ Message.create = create;
155
+ ;
156
+ function from(data) {
157
+ return new MessageConstructor(data);
158
+ }
159
+ Message.from = from;
160
+ })(Message || (exports.Message = Message = {}));
161
+ class MessageConstructor {
162
+ constructor(opts) {
163
+ var _a;
164
+ this.version = MessageConstructor.version;
165
+ this.text = "";
166
+ if (Message.isMessage(opts)) {
167
+ const json = opts;
168
+ this.version = json.version;
169
+ this.text = json.text;
170
+ this.group = json.group;
171
+ this.attachments = json.attachments;
172
+ }
173
+ else if (!opts) {
174
+ this.text = "";
175
+ }
176
+ else if (opts instanceof Uint8Array) {
177
+ const arr = (0, utils_1.encodeUTF8)(opts).split(',');
178
+ this.version = arr[0];
179
+ this.text = (_a = arr[1]) !== null && _a !== void 0 ? _a : "";
180
+ this.group = arr[2];
181
+ this.attachments = arr[3] ? JSON.parse(arr[3]) : undefined;
182
+ }
183
+ else if (typeof opts === 'object') {
184
+ const { text, group, attachments } = opts;
185
+ this.text = text || "";
186
+ this.group = group;
187
+ this.attachments = attachments || [];
188
+ }
189
+ else {
190
+ throw new Error('Invalid constructor arguments for Message');
191
+ }
192
+ }
193
+ get length() { return this.encode().length; }
194
+ setText(str) {
195
+ this.text = str;
196
+ return this;
197
+ }
198
+ setGroup(str) {
199
+ this.group = str;
200
+ return this;
201
+ }
202
+ addAttachment(obj) {
203
+ if (this.attachments)
204
+ this.attachments.push(obj);
205
+ else
206
+ this.attachments = [obj];
207
+ return this;
208
+ }
209
+ delAttachment(obj) {
210
+ var _a;
211
+ const index = (_a = this.attachments) === null || _a === void 0 ? void 0 : _a.indexOf(obj);
212
+ if (!index || !this.attachments)
213
+ return undefined;
214
+ this.attachments = this.attachments.filter((v, i) => index !== i);
215
+ return this;
216
+ }
217
+ encode() {
218
+ return (0, utils_1.decodeUTF8)([this.version, this.text, this.group, JSON.stringify(this.attachments || [])].join(','));
219
+ }
220
+ toString() {
221
+ return this.toJSON();
222
+ }
223
+ toJSON() {
224
+ return JSON.stringify({
225
+ version: MessageConstructor.version,
226
+ text: this.text,
227
+ group: this.group,
228
+ attachments: JSON.stringify(this.attachments)
229
+ });
230
+ }
231
+ }
232
+ MessageConstructor.version = 1;