@freesignal/protocol 0.1.7 → 0.2.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/api.d.ts +38 -0
- package/api.js +118 -0
- package/double-ratchet.d.ts +1 -56
- package/double-ratchet.js +11 -98
- package/index.d.ts +12 -5
- package/index.js +11 -21
- package/package.json +4 -1
- package/test.js +5 -6
- package/types.d.ts +130 -80
- package/types.js +269 -12
- package/x3dh.d.ts +2 -3
- package/x3dh.js +10 -10
- package/crypto.d.ts +0 -24
- package/crypto.js +0 -156
- package/data.d.ts +0 -67
- package/data.js +0 -181
- package/utils.d.ts +0 -78
- package/utils.js +0 -146
package/types.d.ts
CHANGED
|
@@ -16,101 +16,151 @@
|
|
|
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
|
|
19
|
+
import { Encodable } from "@freesignal/interfaces";
|
|
20
|
+
export type UserId = string;
|
|
21
|
+
export interface IdentityKeys {
|
|
22
|
+
readonly publicKey: string;
|
|
23
|
+
readonly identityKey: string;
|
|
24
|
+
}
|
|
25
|
+
export declare namespace IdentityKeys {
|
|
26
|
+
const keyLength: number;
|
|
27
|
+
function isIdentityKeys(obj: any): boolean;
|
|
28
|
+
function from(identityKeys: IdentityKeys): IdentityKeysConstructor;
|
|
29
|
+
}
|
|
30
|
+
declare class IdentityKeysConstructor implements IdentityKeys, Encodable {
|
|
31
|
+
readonly publicKey: string;
|
|
32
|
+
readonly identityKey: string;
|
|
33
|
+
constructor(identityKeys: IdentityKeys | Uint8Array | string);
|
|
21
34
|
encode(): Uint8Array;
|
|
22
35
|
toString(): string;
|
|
23
36
|
toJSON(): string;
|
|
24
37
|
}
|
|
25
|
-
export declare
|
|
26
|
-
|
|
38
|
+
export declare enum Protocols {
|
|
39
|
+
NULL = "",
|
|
40
|
+
MESSAGE = "/freesignal/message/1.0.0",
|
|
41
|
+
RELAY = "/freesignal/relay/1.0.0",
|
|
42
|
+
HANDSHAKE = "/freesignal/handshake/1.0.0"
|
|
27
43
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
entries(): Promise<LocalStorageIterator<[K, T]>>;
|
|
44
|
+
export declare namespace Protocols {
|
|
45
|
+
function isProtocol(protocol: any): boolean;
|
|
46
|
+
function fromCode(code: number): Protocols;
|
|
47
|
+
function toCode(protocol: Protocols): number;
|
|
48
|
+
function encode(protocol: Protocols, length?: number): Uint8Array;
|
|
49
|
+
function decode(array: Uint8Array): Protocols;
|
|
35
50
|
}
|
|
36
|
-
export interface
|
|
51
|
+
export interface Datagram {
|
|
52
|
+
readonly id: string;
|
|
37
53
|
readonly version: number;
|
|
38
|
-
readonly
|
|
39
|
-
readonly
|
|
40
|
-
readonly
|
|
41
|
-
readonly
|
|
42
|
-
|
|
54
|
+
readonly sender: string;
|
|
55
|
+
readonly receiver: string;
|
|
56
|
+
readonly protocol: Protocols;
|
|
57
|
+
readonly createdAt: number;
|
|
58
|
+
payload?: Uint8Array;
|
|
43
59
|
}
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
readonly signedPreKeyHash: string;
|
|
50
|
-
readonly onetimePreKeyHash: string;
|
|
51
|
-
readonly associatedData: string;
|
|
60
|
+
export declare namespace Datagram {
|
|
61
|
+
const version = 1;
|
|
62
|
+
function create(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable): DatagramConstructor;
|
|
63
|
+
function isDatagram(obj: any): boolean;
|
|
64
|
+
function from(data: Uint8Array | Datagram | string): DatagramConstructor;
|
|
52
65
|
}
|
|
53
|
-
|
|
66
|
+
declare class DatagramConstructor implements Encodable, Datagram {
|
|
67
|
+
readonly id: string;
|
|
54
68
|
readonly version: number;
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
57
|
-
readonly
|
|
58
|
-
readonly
|
|
59
|
-
|
|
69
|
+
readonly sender: string;
|
|
70
|
+
readonly receiver: string;
|
|
71
|
+
readonly protocol: Protocols;
|
|
72
|
+
readonly createdAt: number;
|
|
73
|
+
payload?: Uint8Array;
|
|
74
|
+
private static headerOffset;
|
|
75
|
+
constructor(sender: Uint8Array | string, receiver: Uint8Array | string, protocol: Protocols, payload?: Uint8Array | Encodable);
|
|
76
|
+
constructor(data: Uint8Array | Datagram);
|
|
77
|
+
encode(compression?: boolean): Uint8Array;
|
|
78
|
+
encodeSigned(secretKey: Uint8Array, compression?: boolean): Uint8Array;
|
|
79
|
+
toString(): string;
|
|
80
|
+
toJSON(): string;
|
|
60
81
|
}
|
|
61
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Interface representing an encrypted payload.
|
|
84
|
+
* Provides metadata and de/serialization methods.
|
|
85
|
+
*/
|
|
86
|
+
export interface EncryptedData extends Encodable {
|
|
87
|
+
/**
|
|
88
|
+
* The length of the payload.
|
|
89
|
+
*/
|
|
90
|
+
readonly length: number;
|
|
91
|
+
/**
|
|
92
|
+
* Version of the payload.
|
|
93
|
+
*/
|
|
94
|
+
readonly version: number;
|
|
95
|
+
/**
|
|
96
|
+
* The current message count of the sending chain.
|
|
97
|
+
*/
|
|
98
|
+
readonly count: number;
|
|
99
|
+
/**
|
|
100
|
+
* The count of the previous sending chain.
|
|
101
|
+
*/
|
|
102
|
+
readonly previous: number;
|
|
103
|
+
/**
|
|
104
|
+
* The sender's public key used for this message.
|
|
105
|
+
*/
|
|
106
|
+
readonly publicKey: Uint8Array;
|
|
107
|
+
/**
|
|
108
|
+
* The nonce used during encryption.
|
|
109
|
+
*/
|
|
110
|
+
readonly nonce: Uint8Array;
|
|
111
|
+
/**
|
|
112
|
+
* The encrypted message content.
|
|
113
|
+
*/
|
|
114
|
+
readonly ciphertext: Uint8Array;
|
|
115
|
+
/**
|
|
116
|
+
* Serializes the payload into a Uint8Array for transport.
|
|
117
|
+
*/
|
|
118
|
+
encode(): Uint8Array;
|
|
119
|
+
/**
|
|
120
|
+
* Returns the payload as a Base64 string.
|
|
121
|
+
*/
|
|
62
122
|
toString(): string;
|
|
123
|
+
/**
|
|
124
|
+
* Returns the decoded object as a JSON string.
|
|
125
|
+
*/
|
|
63
126
|
toJSON(): string;
|
|
64
|
-
toBuffer(): Uint8Array;
|
|
65
127
|
}
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
readonly UUID: Crypto.UUID;
|
|
75
|
-
randomBytes(n: number): Uint8Array;
|
|
76
|
-
scalarMult(n: Uint8Array, p: Uint8Array): Uint8Array;
|
|
128
|
+
export declare class EncryptedData {
|
|
129
|
+
/**
|
|
130
|
+
* Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
|
|
131
|
+
*
|
|
132
|
+
* @param array - A previously serialized encrypted payload.
|
|
133
|
+
* @returns An instance of `EncryptedPayload`.
|
|
134
|
+
*/
|
|
135
|
+
static from(array: Uint8Array | EncryptedData): EncryptedData;
|
|
77
136
|
}
|
|
78
|
-
export declare
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
137
|
+
export declare class EncryptedDataConstructor implements EncryptedData {
|
|
138
|
+
static readonly secretKeyLength: number;
|
|
139
|
+
static readonly publicKeyLength: number;
|
|
140
|
+
static readonly keyLength: number;
|
|
141
|
+
static readonly nonceLength: number;
|
|
142
|
+
static readonly maxCount = 65536;
|
|
143
|
+
static readonly countLength = 2;
|
|
144
|
+
private raw;
|
|
145
|
+
constructor(count: number | Uint8Array, previous: number | Uint8Array, publicKey: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array, version?: number | Uint8Array);
|
|
146
|
+
constructor(encrypted: Uint8Array | EncryptedData);
|
|
147
|
+
get length(): number;
|
|
148
|
+
get version(): number;
|
|
149
|
+
get count(): number;
|
|
150
|
+
get previous(): number;
|
|
151
|
+
get publicKey(): Uint8Array;
|
|
152
|
+
get nonce(): Uint8Array;
|
|
153
|
+
get ciphertext(): Uint8Array;
|
|
154
|
+
encode(): Uint8Array;
|
|
155
|
+
decode(): {
|
|
156
|
+
version: number;
|
|
157
|
+
count: number;
|
|
158
|
+
previous: number;
|
|
159
|
+
publicKey: string;
|
|
160
|
+
nonce: string;
|
|
161
|
+
ciphertext: string;
|
|
84
162
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
interface box {
|
|
89
|
-
readonly keyLength: number;
|
|
90
|
-
readonly nonceLength: number;
|
|
91
|
-
encrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
92
|
-
decrypt(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | undefined;
|
|
93
|
-
}
|
|
94
|
-
interface ECDH {
|
|
95
|
-
readonly publicKeyLength: number;
|
|
96
|
-
readonly secretKeyLength: number;
|
|
97
|
-
keyPair(secretKey?: Uint8Array): KeyPair;
|
|
98
|
-
sharedKey(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
99
|
-
}
|
|
100
|
-
interface EdDSA {
|
|
101
|
-
readonly publicKeyLength: number;
|
|
102
|
-
readonly secretKeyLength: number;
|
|
103
|
-
readonly signatureLength: number;
|
|
104
|
-
readonly seedLength: number;
|
|
105
|
-
keyPair(secretKey?: Uint8Array): KeyPair;
|
|
106
|
-
keyPairFromSeed(seed: Uint8Array): KeyPair;
|
|
107
|
-
sign(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
108
|
-
verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
|
|
109
|
-
}
|
|
110
|
-
interface UUID {
|
|
111
|
-
generate(): UUIDv4;
|
|
112
|
-
stringify(arr: Uint8Array, offset?: number): string;
|
|
113
|
-
parse(uuid: string): Uint8Array;
|
|
114
|
-
}
|
|
163
|
+
toString(): string;
|
|
164
|
+
toJSON(): string;
|
|
115
165
|
}
|
|
116
166
|
export {};
|
package/types.js
CHANGED
|
@@ -17,16 +17,273 @@
|
|
|
17
17
|
* You should have received a copy of the GNU General Public License
|
|
18
18
|
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
19
19
|
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
20
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
exports.EncryptedDataConstructor = exports.EncryptedData = exports.Datagram = exports.Protocols = exports.IdentityKeys = void 0;
|
|
25
|
+
const utils_1 = require("@freesignal/utils");
|
|
26
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
27
|
+
const fflate_1 = __importDefault(require("fflate"));
|
|
28
|
+
const double_ratchet_1 = require("./double-ratchet");
|
|
29
|
+
var IdentityKeys;
|
|
30
|
+
(function (IdentityKeys) {
|
|
31
|
+
IdentityKeys.keyLength = crypto_1.default.ECDH.publicKeyLength;
|
|
32
|
+
function isIdentityKeys(obj) {
|
|
33
|
+
return (typeof obj === 'object' && obj.publicKey && obj.identityKey);
|
|
34
|
+
}
|
|
35
|
+
IdentityKeys.isIdentityKeys = isIdentityKeys;
|
|
36
|
+
function from(identityKeys) {
|
|
37
|
+
return new IdentityKeysConstructor(identityKeys);
|
|
38
|
+
}
|
|
39
|
+
IdentityKeys.from = from;
|
|
40
|
+
})(IdentityKeys || (exports.IdentityKeys = IdentityKeys = {}));
|
|
41
|
+
class IdentityKeysConstructor {
|
|
42
|
+
constructor(identityKeys) {
|
|
43
|
+
if (typeof identityKeys === 'string')
|
|
44
|
+
identityKeys = (0, utils_1.decodeBase64)(identityKeys);
|
|
45
|
+
if (identityKeys instanceof Uint8Array) {
|
|
46
|
+
this.publicKey = (0, utils_1.encodeBase64)(identityKeys.subarray(0, IdentityKeys.keyLength));
|
|
47
|
+
this.identityKey = (0, utils_1.encodeBase64)(identityKeys.subarray(IdentityKeys.keyLength));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.publicKey = identityKeys.publicKey;
|
|
51
|
+
this.identityKey = identityKeys.identityKey;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
encode() {
|
|
55
|
+
return (0, utils_1.concatUint8Array)((0, utils_1.decodeBase64)(this.publicKey), (0, utils_1.decodeBase64)(this.identityKey));
|
|
56
|
+
}
|
|
57
|
+
toString() {
|
|
58
|
+
throw (0, utils_1.encodeBase64)(this.encode());
|
|
59
|
+
}
|
|
60
|
+
toJSON() {
|
|
61
|
+
throw this.toString();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
var Protocols;
|
|
65
|
+
(function (Protocols) {
|
|
66
|
+
Protocols["NULL"] = "";
|
|
67
|
+
Protocols["MESSAGE"] = "/freesignal/message/1.0.0";
|
|
68
|
+
Protocols["RELAY"] = "/freesignal/relay/1.0.0";
|
|
69
|
+
Protocols["HANDSHAKE"] = "/freesignal/handshake/1.0.0";
|
|
70
|
+
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
71
|
+
(function (Protocols) {
|
|
72
|
+
function isProtocol(protocol) {
|
|
73
|
+
return Object.values(Protocols).includes(protocol);
|
|
74
|
+
}
|
|
75
|
+
Protocols.isProtocol = isProtocol;
|
|
76
|
+
function fromCode(code) {
|
|
77
|
+
return Object.values(Protocols)[code];
|
|
78
|
+
}
|
|
79
|
+
Protocols.fromCode = fromCode;
|
|
80
|
+
function toCode(protocol) {
|
|
81
|
+
return Object.values(Protocols).indexOf(protocol);
|
|
82
|
+
}
|
|
83
|
+
Protocols.toCode = toCode;
|
|
84
|
+
function encode(protocol, length) {
|
|
85
|
+
/*const raw = numberToUint8Array(Protocols.toCode(protocol), length).reverse();
|
|
86
|
+
raw[0] |= (raw.length - 1) << 6;
|
|
87
|
+
return raw;*/
|
|
88
|
+
return (0, utils_1.numberToUint8Array)(Protocols.toCode(protocol), length);
|
|
89
|
+
}
|
|
90
|
+
Protocols.encode = encode;
|
|
91
|
+
function decode(array) {
|
|
92
|
+
//array[0] &= 63;
|
|
93
|
+
//array = array.reverse();
|
|
94
|
+
return Protocols.fromCode((0, utils_1.numberFromUint8Array)(array));
|
|
95
|
+
}
|
|
96
|
+
Protocols.decode = decode;
|
|
97
|
+
})(Protocols || (exports.Protocols = Protocols = {}));
|
|
98
|
+
var Datagram;
|
|
99
|
+
(function (Datagram) {
|
|
100
|
+
Datagram.version = 1;
|
|
101
|
+
function create(sender, receiver, protocol, payload) {
|
|
102
|
+
return new DatagramConstructor(sender, receiver, protocol, payload);
|
|
103
|
+
}
|
|
104
|
+
Datagram.create = create;
|
|
105
|
+
function isDatagram(obj) {
|
|
106
|
+
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);
|
|
107
|
+
}
|
|
108
|
+
Datagram.isDatagram = isDatagram;
|
|
109
|
+
function from(data) {
|
|
110
|
+
if (typeof data === 'string') {
|
|
111
|
+
const decoded = (0, utils_1.decodeBase64)(data);
|
|
112
|
+
return new DatagramConstructor(decoded);
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
return new DatagramConstructor(data);
|
|
116
|
+
}
|
|
117
|
+
Datagram.from = from;
|
|
118
|
+
})(Datagram || (exports.Datagram = Datagram = {}));
|
|
119
|
+
class DatagramConstructor {
|
|
120
|
+
constructor(data, receiver, protocol, payload) {
|
|
121
|
+
if (!receiver && !protocol && !payload) {
|
|
122
|
+
if (data instanceof Uint8Array) {
|
|
123
|
+
this.version = data[0] & 63;
|
|
124
|
+
this.protocol = Protocols.decode(data.subarray(1, 2));
|
|
125
|
+
this.id = crypto_1.default.UUID.stringify(data.subarray(2, 18));
|
|
126
|
+
this.createdAt = (0, utils_1.numberFromUint8Array)(data.subarray(18, 26));
|
|
127
|
+
this.sender = (0, utils_1.encodeBase64)(data.subarray(26, 26 + crypto_1.default.EdDSA.publicKeyLength));
|
|
128
|
+
this.receiver = (0, utils_1.encodeBase64)(data.subarray(26 + crypto_1.default.EdDSA.publicKeyLength, DatagramConstructor.headerOffset));
|
|
129
|
+
if (data[0] & 128) {
|
|
130
|
+
const signature = data.subarray(data.length - crypto_1.default.EdDSA.signatureLength);
|
|
131
|
+
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)))
|
|
132
|
+
throw new Error('Invalid signature for Datagram');
|
|
133
|
+
}
|
|
134
|
+
if (data[0] & 64)
|
|
135
|
+
this.payload = fflate_1.default.inflateSync(data.subarray(DatagramConstructor.headerOffset, data.length));
|
|
136
|
+
else
|
|
137
|
+
this.payload = data.subarray(DatagramConstructor.headerOffset, data.length);
|
|
138
|
+
}
|
|
139
|
+
else if (Datagram.isDatagram(data)) {
|
|
140
|
+
const datagram = data;
|
|
141
|
+
this.id = datagram.id;
|
|
142
|
+
this.version = datagram.version;
|
|
143
|
+
this.sender = datagram.sender;
|
|
144
|
+
this.receiver = datagram.receiver;
|
|
145
|
+
this.protocol = datagram.protocol;
|
|
146
|
+
this.createdAt = datagram.createdAt;
|
|
147
|
+
this.payload = datagram.payload;
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
throw new Error('Invalid constructor arguments for Datagram');
|
|
151
|
+
}
|
|
152
|
+
else if (typeof data === 'string' || data instanceof Uint8Array) {
|
|
153
|
+
this.id = crypto_1.default.UUID.generate().toString();
|
|
154
|
+
this.version = Datagram.version;
|
|
155
|
+
this.sender = typeof data === 'string' ? data : (0, utils_1.encodeBase64)(data);
|
|
156
|
+
this.receiver = typeof receiver === 'string' ? receiver : (0, utils_1.encodeBase64)(receiver);
|
|
157
|
+
this.protocol = protocol;
|
|
158
|
+
this.createdAt = Date.now();
|
|
159
|
+
this.payload = payload instanceof Uint8Array ? payload : payload === null || payload === void 0 ? void 0 : payload.encode();
|
|
160
|
+
}
|
|
161
|
+
else
|
|
162
|
+
throw new Error('Invalid constructor arguments for Datagram');
|
|
163
|
+
}
|
|
164
|
+
encode(compression = true) {
|
|
165
|
+
var _a;
|
|
166
|
+
compression = compression && this.payload != undefined && this.payload.length > 1024;
|
|
167
|
+
return (0, utils_1.concatUint8Array)(new Uint8Array(1).fill(this.version | (compression ? 64 : 0)), //1
|
|
168
|
+
Protocols.encode(this.protocol), //1
|
|
169
|
+
(_a = crypto_1.default.UUID.parse(this.id)) !== null && _a !== void 0 ? _a : [], //16
|
|
170
|
+
(0, utils_1.numberToUint8Array)(this.createdAt, 8), //8
|
|
171
|
+
(0, utils_1.decodeBase64)(this.sender), //32
|
|
172
|
+
(0, utils_1.decodeBase64)(this.receiver), //32
|
|
173
|
+
...(this.payload ? [compression ? fflate_1.default.deflateSync(this.payload) : this.payload] : []));
|
|
174
|
+
}
|
|
175
|
+
encodeSigned(secretKey, compression) {
|
|
176
|
+
//if (!this.payload) throw new Error('Cannot sign a datagram without payload');
|
|
177
|
+
const header = this.encode(compression);
|
|
178
|
+
header[0] |= 128; // Set the sign bit
|
|
179
|
+
const signature = crypto_1.default.EdDSA.sign(header, secretKey);
|
|
180
|
+
return (0, utils_1.concatUint8Array)(header, signature);
|
|
181
|
+
}
|
|
182
|
+
toString() {
|
|
183
|
+
return (0, utils_1.encodeBase64)(this.encode());
|
|
184
|
+
}
|
|
185
|
+
toJSON() {
|
|
186
|
+
/*return JSON.stringify({
|
|
187
|
+
id: this.id,
|
|
188
|
+
version: this.version,
|
|
189
|
+
senderKey: this.senderKey,
|
|
190
|
+
senderRelay: this.senderRelay,
|
|
191
|
+
receiverKey: this.receiverKey,
|
|
192
|
+
receiverRelay: this.receiverRelay,
|
|
193
|
+
protocol: this.protocol,
|
|
194
|
+
createdAt: this.createdAt,
|
|
195
|
+
payload: this.payload ? encodeBase64(this.payload) : undefined
|
|
196
|
+
});*/
|
|
197
|
+
return this.toString();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
DatagramConstructor.headerOffset = 26 + crypto_1.default.EdDSA.publicKeyLength * 2;
|
|
201
|
+
class EncryptedData {
|
|
202
|
+
/**
|
|
203
|
+
* Static factory method that constructs an `EncryptedPayload` from a raw Uint8Array.
|
|
204
|
+
*
|
|
205
|
+
* @param array - A previously serialized encrypted payload.
|
|
206
|
+
* @returns An instance of `EncryptedPayload`.
|
|
207
|
+
*/
|
|
208
|
+
static from(array) {
|
|
209
|
+
return new EncryptedDataConstructor(array);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.EncryptedData = EncryptedData;
|
|
213
|
+
class EncryptedDataConstructor {
|
|
214
|
+
constructor(...arrays) {
|
|
215
|
+
arrays = arrays.filter(value => value !== undefined);
|
|
216
|
+
if (arrays[0] instanceof EncryptedDataConstructor) {
|
|
217
|
+
this.raw = arrays[0].raw;
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
if (typeof arrays[0] === 'number')
|
|
221
|
+
arrays[0] = (0, utils_1.numberToUint8Array)(arrays[0], EncryptedDataConstructor.countLength);
|
|
222
|
+
if (typeof arrays[1] === 'number')
|
|
223
|
+
arrays[1] = (0, utils_1.numberToUint8Array)(arrays[1], EncryptedDataConstructor.countLength);
|
|
224
|
+
if (arrays.length === 6) {
|
|
225
|
+
arrays.unshift(typeof arrays[5] === 'number' ? (0, utils_1.numberToUint8Array)(arrays[5]) : arrays[5]);
|
|
226
|
+
arrays.pop();
|
|
227
|
+
}
|
|
228
|
+
else if (arrays.length > 1) {
|
|
229
|
+
arrays.unshift((0, utils_1.numberToUint8Array)(double_ratchet_1.KeySession.version));
|
|
230
|
+
}
|
|
231
|
+
this.raw = (0, utils_1.concatUint8Array)(...arrays);
|
|
232
|
+
}
|
|
233
|
+
get length() { return this.raw.length; }
|
|
234
|
+
get version() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.version.get)); }
|
|
235
|
+
get count() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.count.get)); }
|
|
236
|
+
get previous() { return (0, utils_1.numberFromUint8Array)(new Uint8Array(this.raw.buffer, ...Offsets.previous.get)); }
|
|
237
|
+
get publicKey() { return new Uint8Array(this.raw.buffer, ...Offsets.publicKey.get); }
|
|
238
|
+
get nonce() { return new Uint8Array(this.raw.buffer, ...Offsets.nonce.get); }
|
|
239
|
+
get ciphertext() { return new Uint8Array(this.raw.buffer, Offsets.ciphertext.start); }
|
|
240
|
+
encode() {
|
|
241
|
+
return this.raw;
|
|
242
|
+
}
|
|
243
|
+
decode() {
|
|
244
|
+
return {
|
|
245
|
+
version: this.version,
|
|
246
|
+
count: this.count,
|
|
247
|
+
previous: this.previous,
|
|
248
|
+
publicKey: (0, utils_1.encodeBase64)(this.publicKey),
|
|
249
|
+
nonce: (0, utils_1.encodeBase64)(this.nonce),
|
|
250
|
+
ciphertext: (0, utils_1.encodeBase64)(this.ciphertext)
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
toString() {
|
|
254
|
+
return (0, utils_1.encodeBase64)(this.raw);
|
|
255
|
+
}
|
|
256
|
+
toJSON() {
|
|
257
|
+
return JSON.stringify(this.decode());
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.EncryptedDataConstructor = EncryptedDataConstructor;
|
|
261
|
+
EncryptedDataConstructor.secretKeyLength = crypto_1.default.ECDH.secretKeyLength;
|
|
262
|
+
EncryptedDataConstructor.publicKeyLength = crypto_1.default.ECDH.publicKeyLength;
|
|
263
|
+
EncryptedDataConstructor.keyLength = crypto_1.default.box.keyLength;
|
|
264
|
+
EncryptedDataConstructor.nonceLength = crypto_1.default.box.nonceLength;
|
|
265
|
+
EncryptedDataConstructor.maxCount = 65536; //32768;
|
|
266
|
+
EncryptedDataConstructor.countLength = 2;
|
|
267
|
+
class Offsets {
|
|
268
|
+
static set(start, length) {
|
|
269
|
+
class Offset {
|
|
270
|
+
constructor(start, length) {
|
|
271
|
+
this.start = start;
|
|
272
|
+
this.length = length;
|
|
273
|
+
if (typeof length === 'number')
|
|
274
|
+
this.end = start + length;
|
|
275
|
+
}
|
|
276
|
+
get get() {
|
|
277
|
+
return [this.start, this.length];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return new Offset(start, length);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
Offsets.checksum = Offsets.set(0, 0);
|
|
284
|
+
Offsets.version = Offsets.set(Offsets.checksum.end, 1);
|
|
285
|
+
Offsets.count = Offsets.set(Offsets.version.end, EncryptedDataConstructor.countLength);
|
|
286
|
+
Offsets.previous = Offsets.set(Offsets.count.end, EncryptedDataConstructor.countLength);
|
|
287
|
+
Offsets.publicKey = Offsets.set(Offsets.previous.end, EncryptedDataConstructor.publicKeyLength);
|
|
288
|
+
Offsets.nonce = Offsets.set(Offsets.publicKey.end, EncryptedDataConstructor.nonceLength);
|
|
289
|
+
Offsets.ciphertext = Offsets.set(Offsets.nonce.end, undefined);
|
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 "@freesignal/interfaces";
|
|
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
|
@@ -31,9 +31,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
31
31
|
};
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
33
|
exports.KeyExchange = void 0;
|
|
34
|
-
const crypto_1 = __importDefault(require("
|
|
34
|
+
const crypto_1 = __importDefault(require("@freesignal/crypto"));
|
|
35
35
|
const double_ratchet_1 = require("./double-ratchet");
|
|
36
|
-
const utils_1 = require("
|
|
36
|
+
const utils_1 = require("@freesignal/utils");
|
|
37
37
|
class KeyExchange {
|
|
38
38
|
constructor(signSecretKey, boxSecretKey, bundleStore) {
|
|
39
39
|
this._signatureKey = crypto_1.default.EdDSA.keyPair(signSecretKey);
|
|
@@ -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));
|
package/crypto.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
import { Crypto } from './types';
|
|
20
|
-
declare const crypto: Crypto;
|
|
21
|
-
declare namespace crypto {
|
|
22
|
-
type KeyPair = Crypto.KeyPair;
|
|
23
|
-
}
|
|
24
|
-
export default crypto;
|