This repository has been archived by the owner on Mar 9, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathlib.loki_crypt.js
149 lines (132 loc) · 4.38 KB
/
lib.loki_crypt.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const crypto = require('crypto');
const libsignal = require('libsignal');
const bb = require('bytebuffer');
/*
bufferFrom64
bufferTo64
bufferFromHex
bufferToHex
*/
const IV_LENGTH = 16;
const NONCE_LENGTH = 12;
const TAG_LENGTH = 16;
async function DHEncrypt(symmetricKey, plainText) {
const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const ciphertext = await libsignal.crypto.encrypt(
symmetricKey,
plainText,
iv
);
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
return ivAndCiphertext;
}
async function DHDecrypt(symmetricKey, ivAndCiphertext) {
const iv = ivAndCiphertext.slice(0, IV_LENGTH);
const ciphertext = ivAndCiphertext.slice(IV_LENGTH);
return libsignal.crypto.decrypt(symmetricKey, ciphertext, iv);
}
// used for proxy requests
const DHEncrypt64 = async (symmetricKey, plainText) => {
// generate an iv (web-friendly)
const iv = crypto.randomBytes(IV_LENGTH);
// encrypt plainText
const ciphertext = await libsignal.crypto.encrypt(
symmetricKey,
plainText,
iv
);
// create buffer
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
// copy iv into buffer
ivAndCiphertext.set(new Uint8Array(iv));
// copy ciphertext into buffer
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
// base64 encode
return bb.wrap(ivAndCiphertext).toString('base64');
}
// used for tokens
const DHDecrypt64 = async (symmetricKey, cipherText64) => {
// base64 decode
const ivAndCiphertext = Buffer.from(
bb.wrap(cipherText64, 'base64').toArrayBuffer()
);
// extract iv
const iv = ivAndCiphertext.slice(0, IV_LENGTH);
// extract ciphertext
const ciphertext = ivAndCiphertext.slice(IV_LENGTH);
// decode plaintext
return libsignal.crypto.decrypt(symmetricKey, ciphertext, iv);
}
function makeSymmetricKey(privKey, pubKey) {
if (pubKey.byteLength === 32) {
pubKey = Buffer.concat([Buffer.from('05', 'hex'), pubKey])
}
const keyAgreement = libsignal.curve.calculateAgreement(
pubKey,
privKey,
);
//console_wrapper.log('makeSymmetricKey agreement', keyAgreement.toString('hex'))
// hash the key agreement
const hashedSymmetricKeyBuf = crypto.createHmac('sha256', 'LOKI').update(keyAgreement).digest()
return hashedSymmetricKeyBuf;
}
function encryptGCM(symmetricKey, plaintextEnc) {
// not on the node side
//const nonce = libsignal.crypto.getRandomBytes(NONCE_LENGTH);
const nonce = crypto.randomBytes(NONCE_LENGTH); // Buffer (object)
const cipher = crypto.createCipheriv('aes-256-gcm', symmetricKey, nonce);
const ciphertext = Buffer.concat([cipher.update(plaintextEnc), cipher.final()]);
const tag = cipher.getAuthTag()
const finalBuf = Buffer.concat([nonce, ciphertext, tag]);
return finalBuf;
}
function decryptGCM(symmetricKey, ivCiphertextAndTag) {
const nonce = ivCiphertextAndTag.slice(0, NONCE_LENGTH);
const ciphertext = ivCiphertextAndTag.slice(NONCE_LENGTH, ivCiphertextAndTag.byteLength - TAG_LENGTH);
const tag = ivCiphertextAndTag.slice(ivCiphertextAndTag.byteLength - TAG_LENGTH);
const decipher = crypto.createDecipheriv('aes-256-gcm', symmetricKey, nonce);
decipher.setAuthTag(tag);
return decipher.update(ciphertext, 'binary', 'utf8') + decipher.final();
}
// FIXME: bring in the multidevice support functions
// or maybe put them into a separate libraries
// reply_to if 0 not, is also required in adnMessage
async function getSigData(sigVer, privKey, noteValue, adnMessage) {
let sigString = '';
sigString += adnMessage.text.trim();
sigString += noteValue.timestamp;
if (noteValue.quote) {
sigString += noteValue.quote.id;
sigString += noteValue.quote.author;
sigString += noteValue.quote.text.trim();
if (adnMessage.reply_to) {
sigString += adnMessage.reply_to;
}
}
/*
sigString += [...attachmentAnnotations, ...previewAnnotations]
.map(data => data.id || data.image.id)
.sort()
.join();
*/
sigString += sigVer;
const sigData = Buffer.from(bb.wrap(sigString, 'utf8').toArrayBuffer());
const sig = await libsignal.curve.calculateSignature(privKey, sigData);
return sig.toString('hex')
}
module.exports = {
DHEncrypt,
DHDecrypt,
DHEncrypt64,
DHDecrypt64,
makeSymmetricKey,
encryptGCM,
decryptGCM,
getSigData,
}