orgnote-api 0.14.2 → 0.15.1
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/encryption/__tests__/__snapshots__/note-encryption.spec.ts.snap +48 -6
- package/encryption/__tests__/encryption.spec.ts +145 -21
- package/encryption/__tests__/note-encryption.spec.ts +103 -25
- package/encryption/encryption.ts +109 -60
- package/encryption/note-encryption.ts +21 -113
- package/models/encryption.ts +28 -0
- package/package.json +1 -1
- package/tools/is-gpg-encrypted.ts +4 -1
|
@@ -4,7 +4,6 @@ exports[`Should decrypt note content via password 1`] = `
|
|
|
4
4
|
[
|
|
5
5
|
{
|
|
6
6
|
"encrypted": false,
|
|
7
|
-
"encryptionType": "gpgPassword",
|
|
8
7
|
"id": "id",
|
|
9
8
|
"meta": {
|
|
10
9
|
"headings": [
|
|
@@ -28,7 +27,6 @@ exports[`Should decrypt note via provided keys 1`] = `
|
|
|
28
27
|
[
|
|
29
28
|
{
|
|
30
29
|
"encrypted": false,
|
|
31
|
-
"encryptionType": "gpgKeys",
|
|
32
30
|
"id": "id",
|
|
33
31
|
"meta": {
|
|
34
32
|
"headings": [
|
|
@@ -48,6 +46,22 @@ exports[`Should decrypt note via provided keys 1`] = `
|
|
|
48
46
|
]
|
|
49
47
|
`;
|
|
50
48
|
|
|
49
|
+
exports[`Should encrypt note and decrypt it into binary format 1`] = `
|
|
50
|
+
{
|
|
51
|
+
"author": {
|
|
52
|
+
"email": "test@mail.com",
|
|
53
|
+
"id": "1",
|
|
54
|
+
"name": "John Doe",
|
|
55
|
+
},
|
|
56
|
+
"encrypted": true,
|
|
57
|
+
"id": "id",
|
|
58
|
+
"meta": {
|
|
59
|
+
"id": undefined,
|
|
60
|
+
"published": false,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
|
|
51
65
|
exports[`Should encrypt note content via password 1`] = `
|
|
52
66
|
{
|
|
53
67
|
"author": {
|
|
@@ -56,7 +70,38 @@ exports[`Should encrypt note content via password 1`] = `
|
|
|
56
70
|
"name": "John Doe",
|
|
57
71
|
},
|
|
58
72
|
"encrypted": true,
|
|
59
|
-
"
|
|
73
|
+
"id": "id",
|
|
74
|
+
"meta": {
|
|
75
|
+
"id": undefined,
|
|
76
|
+
"published": false,
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
exports[`Should encrypt note content via password to binary format 1`] = `
|
|
82
|
+
{
|
|
83
|
+
"author": {
|
|
84
|
+
"email": "test@mail.com",
|
|
85
|
+
"id": "1",
|
|
86
|
+
"name": "John Doe",
|
|
87
|
+
},
|
|
88
|
+
"encrypted": true,
|
|
89
|
+
"id": "id",
|
|
90
|
+
"meta": {
|
|
91
|
+
"id": undefined,
|
|
92
|
+
"published": false,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
exports[`Should encrypt note to binary format 1`] = `
|
|
98
|
+
{
|
|
99
|
+
"author": {
|
|
100
|
+
"email": "test@mail.com",
|
|
101
|
+
"id": "1",
|
|
102
|
+
"name": "John Doe",
|
|
103
|
+
},
|
|
104
|
+
"encrypted": true,
|
|
60
105
|
"id": "id",
|
|
61
106
|
"meta": {
|
|
62
107
|
"id": undefined,
|
|
@@ -73,7 +118,6 @@ exports[`Should encrypt note via keys 1`] = `
|
|
|
73
118
|
"name": "John Doe",
|
|
74
119
|
},
|
|
75
120
|
"encrypted": true,
|
|
76
|
-
"encryptionType": "gpgKeys",
|
|
77
121
|
"id": "id",
|
|
78
122
|
"meta": {
|
|
79
123
|
"id": undefined,
|
|
@@ -130,7 +174,6 @@ exports[`Should not encrypt public note 1`] = `
|
|
|
130
174
|
"name": "John Doe",
|
|
131
175
|
},
|
|
132
176
|
"encrypted": false,
|
|
133
|
-
"encryptionType": "gpgPassword",
|
|
134
177
|
"id": "id",
|
|
135
178
|
"meta": {
|
|
136
179
|
"description": "Awesome description",
|
|
@@ -145,7 +188,6 @@ exports[`Should set not encrypted status when params type does not provided 1`]
|
|
|
145
188
|
[
|
|
146
189
|
{
|
|
147
190
|
"encrypted": true,
|
|
148
|
-
"encryptionType": "gpgKeys",
|
|
149
191
|
"id": "id",
|
|
150
192
|
"meta": {
|
|
151
193
|
"description": "Awesome description",
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
IncorrectOrMissingPrivateKeyPasswordError,
|
|
9
9
|
encrypt,
|
|
10
10
|
decrypt,
|
|
11
|
+
_encryptViaKeys,
|
|
12
|
+
armor,
|
|
13
|
+
unarmor,
|
|
11
14
|
} from '../encryption';
|
|
12
15
|
import { test, expect } from 'vitest';
|
|
13
16
|
|
|
@@ -16,21 +19,35 @@ import {
|
|
|
16
19
|
armoredPrivateKey,
|
|
17
20
|
privateKeyPassphrase,
|
|
18
21
|
} from './encryption-keys';
|
|
22
|
+
// import { armor } from 'openpgp';
|
|
23
|
+
|
|
24
|
+
test('Should encrypt text as armored message via keys', async () => {
|
|
25
|
+
const res = await encryptViaKeys({
|
|
26
|
+
content: 'Hello world',
|
|
27
|
+
publicKey: armoredPublicKey,
|
|
28
|
+
privateKey: armoredPrivateKey,
|
|
29
|
+
privateKeyPassphrase: privateKeyPassphrase,
|
|
30
|
+
format: 'armored',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(res.startsWith('-----BEGIN PGP MESSAGE-----')).toBeTruthy();
|
|
34
|
+
});
|
|
19
35
|
|
|
20
36
|
test('Should encrypt text via keys', async () => {
|
|
21
|
-
const res = await encryptViaKeys(
|
|
22
|
-
'Hello world',
|
|
23
|
-
armoredPublicKey,
|
|
24
|
-
armoredPrivateKey,
|
|
25
|
-
privateKeyPassphrase
|
|
26
|
-
|
|
37
|
+
const res = await encryptViaKeys({
|
|
38
|
+
content: 'Hello world',
|
|
39
|
+
publicKey: armoredPublicKey,
|
|
40
|
+
privateKey: armoredPrivateKey,
|
|
41
|
+
privateKeyPassphrase,
|
|
42
|
+
format: 'armored',
|
|
43
|
+
});
|
|
27
44
|
|
|
28
45
|
expect(res.startsWith('-----BEGIN PGP MESSAGE-----')).toBeTruthy();
|
|
29
46
|
});
|
|
30
47
|
|
|
31
48
|
test('Should decrypt via provided keys', async () => {
|
|
32
|
-
const decryptedMessage = await decryptViaKeys(
|
|
33
|
-
`-----BEGIN PGP MESSAGE-----
|
|
49
|
+
const decryptedMessage = await decryptViaKeys({
|
|
50
|
+
content: `-----BEGIN PGP MESSAGE-----
|
|
34
51
|
|
|
35
52
|
wcFMA/vryg+TTn0rARAAhXuEjOHa856iCNVmdeIGHF+IEoeEwTc5tIcr6Lri
|
|
36
53
|
V6xs//3WnwVwUlyxYrum3yCpx8t5gyWTXFfTNH08VoVqPVP45fkk1H7jdC6Q
|
|
@@ -62,15 +79,19 @@ YQ==
|
|
|
62
79
|
=f4F1
|
|
63
80
|
-----END PGP MESSAGE-----
|
|
64
81
|
`,
|
|
65
|
-
armoredPrivateKey,
|
|
66
|
-
privateKeyPassphrase
|
|
67
|
-
);
|
|
82
|
+
privateKey: armoredPrivateKey,
|
|
83
|
+
privateKeyPassphrase,
|
|
84
|
+
});
|
|
68
85
|
expect(decryptedMessage).toEqual('Hello world');
|
|
69
86
|
});
|
|
70
87
|
|
|
71
88
|
test('Should encrypt via password', async () => {
|
|
72
89
|
const password = 'test';
|
|
73
|
-
const res = await encryptViaPassword(
|
|
90
|
+
const res = await encryptViaPassword({
|
|
91
|
+
content: 'Hello world',
|
|
92
|
+
password,
|
|
93
|
+
format: 'armored',
|
|
94
|
+
});
|
|
74
95
|
|
|
75
96
|
expect(res.startsWith('-----BEGIN PGP MESSAGE-----')).toBeTruthy();
|
|
76
97
|
});
|
|
@@ -86,7 +107,7 @@ aGW80jwBXEQ7uTjT8akpOKiH7BIuhEUZIXh+vDveG0Uwf63s2dIklznAEo+E
|
|
|
86
107
|
-----END PGP MESSAGE-----
|
|
87
108
|
`;
|
|
88
109
|
|
|
89
|
-
expect(await decryptViaPassword(encryptedMsg, password)).toEqual(
|
|
110
|
+
expect(await decryptViaPassword({ content: encryptedMsg, password })).toEqual(
|
|
90
111
|
'Hello world'
|
|
91
112
|
);
|
|
92
113
|
});
|
|
@@ -125,14 +146,13 @@ YQ==
|
|
|
125
146
|
-----END PGP MESSAGE-----`;
|
|
126
147
|
|
|
127
148
|
try {
|
|
128
|
-
await decryptViaPassword(encryptedMsg, 'password');
|
|
149
|
+
await decryptViaPassword({ content: encryptedMsg, password: 'password' });
|
|
129
150
|
} catch (e) {
|
|
130
151
|
expect(e).toBeInstanceOf(NoKeysProvidedError);
|
|
131
152
|
}
|
|
132
153
|
});
|
|
133
154
|
|
|
134
155
|
test('Should raise IncorrectOrMissingPrivateKeyPasswordError error when incorrect armored key provided', async () => {
|
|
135
|
-
const password = 'test';
|
|
136
156
|
const encryptedMsg = `-----BEGIN PGP MESSAGE-----
|
|
137
157
|
|
|
138
158
|
wy4ECQMI6KFWGqyVV+DgYl0qUEeTe1kAdjkoR4FxFJxx+6QiOP+sZ6h7bn//
|
|
@@ -143,7 +163,11 @@ aGW80jwBXEQ7uTjT8akpOKiH7BIuhEUZIXh+vDveG0Uwf63s2dIklznAEo+E
|
|
|
143
163
|
`;
|
|
144
164
|
|
|
145
165
|
try {
|
|
146
|
-
await decryptViaKeys(
|
|
166
|
+
await decryptViaKeys({
|
|
167
|
+
content: encryptedMsg,
|
|
168
|
+
publicKey: armoredPublicKey,
|
|
169
|
+
privateKey: privateKeyPassphrase,
|
|
170
|
+
});
|
|
147
171
|
} catch (e) {
|
|
148
172
|
expect(e).toBeInstanceOf(IncorrectOrMissingPrivateKeyPasswordError);
|
|
149
173
|
}
|
|
@@ -160,7 +184,11 @@ aGW80jwBXEQ7uTjT8akpOKiH7BIuhEUZIXh+vDveG0Uwf63s2dIklznAEo+E
|
|
|
160
184
|
`;
|
|
161
185
|
|
|
162
186
|
try {
|
|
163
|
-
await decryptViaKeys(
|
|
187
|
+
await decryptViaKeys({
|
|
188
|
+
content: encryptedMsg,
|
|
189
|
+
privateKey: armoredPrivateKey,
|
|
190
|
+
privateKeyPassphrase,
|
|
191
|
+
});
|
|
164
192
|
} catch (e) {
|
|
165
193
|
expect(e).toBeInstanceOf(NoPasswordProvidedError);
|
|
166
194
|
}
|
|
@@ -170,14 +198,17 @@ test('Should encrypt and decrypt text by provided configs via password', async (
|
|
|
170
198
|
const text = 'Hello world';
|
|
171
199
|
const password = '123';
|
|
172
200
|
|
|
173
|
-
const res = await encrypt(
|
|
201
|
+
const res = await encrypt({
|
|
202
|
+
content: text,
|
|
174
203
|
type: 'gpgPassword',
|
|
175
204
|
password,
|
|
205
|
+
format: 'armored',
|
|
176
206
|
});
|
|
177
207
|
|
|
178
208
|
expect(res.startsWith('-----BEGIN PGP MESSAGE-----')).toBeTruthy();
|
|
179
209
|
|
|
180
|
-
const decryptedMessage = await decrypt(
|
|
210
|
+
const decryptedMessage = await decrypt({
|
|
211
|
+
content: res,
|
|
181
212
|
type: 'gpgPassword',
|
|
182
213
|
password,
|
|
183
214
|
});
|
|
@@ -187,8 +218,10 @@ test('Should encrypt and decrypt text by provided configs via password', async (
|
|
|
187
218
|
|
|
188
219
|
test('Should encrypt and decrypt text by provided configs via keys', async () => {
|
|
189
220
|
const text = 'Hello world';
|
|
190
|
-
const res = await encrypt(
|
|
221
|
+
const res = await encrypt({
|
|
222
|
+
content: text,
|
|
191
223
|
type: 'gpgKeys',
|
|
224
|
+
format: 'armored',
|
|
192
225
|
publicKey: armoredPublicKey,
|
|
193
226
|
privateKey: armoredPrivateKey,
|
|
194
227
|
privateKeyPassphrase,
|
|
@@ -196,12 +229,103 @@ test('Should encrypt and decrypt text by provided configs via keys', async () =>
|
|
|
196
229
|
|
|
197
230
|
expect(res.startsWith('-----BEGIN PGP MESSAGE-----')).toBeTruthy();
|
|
198
231
|
|
|
199
|
-
const decryptedMessage = await decrypt(
|
|
232
|
+
const decryptedMessage = await decrypt({
|
|
233
|
+
content: res,
|
|
200
234
|
type: 'gpgKeys',
|
|
201
235
|
publicKey: armoredPublicKey,
|
|
202
236
|
privateKey: armoredPrivateKey,
|
|
203
237
|
privateKeyPassphrase,
|
|
238
|
+
format: 'utf8',
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(decryptedMessage).toEqual(text);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('Should encrypt to binary and decrypt to format armored!', async () => {
|
|
245
|
+
const text = 'Hello world';
|
|
246
|
+
|
|
247
|
+
const res = await encrypt({
|
|
248
|
+
content: text,
|
|
249
|
+
type: 'gpgPassword',
|
|
250
|
+
password: '123',
|
|
251
|
+
format: 'binary',
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(res).toBeInstanceOf(Uint8Array);
|
|
255
|
+
|
|
256
|
+
const decryptedMessage = await decrypt({
|
|
257
|
+
content: res,
|
|
258
|
+
type: 'gpgPassword',
|
|
259
|
+
format: 'utf8',
|
|
260
|
+
password: '123',
|
|
204
261
|
});
|
|
205
262
|
|
|
206
263
|
expect(decryptedMessage).toEqual(text);
|
|
207
264
|
});
|
|
265
|
+
|
|
266
|
+
test('Should encrypt to binary and decrypt to binary format', async () => {
|
|
267
|
+
const text = 'Hello world';
|
|
268
|
+
|
|
269
|
+
const res = await encrypt({
|
|
270
|
+
content: text,
|
|
271
|
+
type: 'gpgPassword',
|
|
272
|
+
password: '123',
|
|
273
|
+
format: 'binary',
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(res).toBeInstanceOf(Uint8Array);
|
|
277
|
+
|
|
278
|
+
const decryptedMessage = await decrypt({
|
|
279
|
+
content: res,
|
|
280
|
+
type: 'gpgPassword',
|
|
281
|
+
format: 'binary',
|
|
282
|
+
password: '123',
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
expect(decryptedMessage.toString()).toMatchInlineSnapshot(
|
|
286
|
+
`"72,101,108,108,111,32,119,111,114,108,100"`
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('Should encrypt to armored text and decrypt as binary format', async () => {
|
|
291
|
+
const text = 'Hello world';
|
|
292
|
+
|
|
293
|
+
const res = await encrypt({
|
|
294
|
+
content: text,
|
|
295
|
+
type: 'gpgPassword',
|
|
296
|
+
password: '123',
|
|
297
|
+
format: 'armored',
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(res).toBeTypeOf('string');
|
|
301
|
+
|
|
302
|
+
const decryptedMessage = await decrypt({
|
|
303
|
+
content: res,
|
|
304
|
+
type: 'gpgPassword',
|
|
305
|
+
format: 'binary',
|
|
306
|
+
password: '123',
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
expect(decryptedMessage).toBeInstanceOf(Uint8Array);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('Should armor and unarmor encrypted file', async () => {
|
|
313
|
+
const content: Uint8Array = new Uint8Array([
|
|
314
|
+
72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100,
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
const armored = armor(content);
|
|
318
|
+
|
|
319
|
+
expect(armored).toMatchInlineSnapshot(`
|
|
320
|
+
"-----BEGIN PGP MESSAGE-----
|
|
321
|
+
|
|
322
|
+
SGVsbG8gd29ybGQ=
|
|
323
|
+
=7asC
|
|
324
|
+
-----END PGP MESSAGE-----
|
|
325
|
+
"
|
|
326
|
+
`);
|
|
327
|
+
|
|
328
|
+
const { text, data } = await unarmor(armored);
|
|
329
|
+
|
|
330
|
+
expect(data).toEqual(content);
|
|
331
|
+
});
|
|
@@ -17,7 +17,6 @@ test('Should encrypt note content via password', async () => {
|
|
|
17
17
|
|
|
18
18
|
const note: Note = {
|
|
19
19
|
id: 'id',
|
|
20
|
-
encryptionType: 'gpgPassword',
|
|
21
20
|
meta: {
|
|
22
21
|
title: 'My note title',
|
|
23
22
|
images: [],
|
|
@@ -31,9 +30,11 @@ test('Should encrypt note content via password', async () => {
|
|
|
31
30
|
},
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
const [encryptedNote, encryptedNoteText] = await encryptNote(note,
|
|
33
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
34
|
+
content: noteText,
|
|
35
35
|
type: 'gpgPassword',
|
|
36
36
|
password: '123',
|
|
37
|
+
format: 'armored',
|
|
37
38
|
});
|
|
38
39
|
|
|
39
40
|
expect(encryptedNoteText.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
@@ -42,6 +43,39 @@ test('Should encrypt note content via password', async () => {
|
|
|
42
43
|
expect(encryptedNote).toMatchSnapshot();
|
|
43
44
|
});
|
|
44
45
|
|
|
46
|
+
test('Should encrypt note content via password to binary format', async () => {
|
|
47
|
+
const noteText = `#+ID: qweqwe
|
|
48
|
+
|
|
49
|
+
#+TITLE: Hello worlld
|
|
50
|
+
|
|
51
|
+
* Hello?`;
|
|
52
|
+
|
|
53
|
+
const note: Note = {
|
|
54
|
+
id: 'id',
|
|
55
|
+
meta: {
|
|
56
|
+
title: 'My note title',
|
|
57
|
+
images: [],
|
|
58
|
+
published: false,
|
|
59
|
+
description: 'Awesome description',
|
|
60
|
+
},
|
|
61
|
+
author: {
|
|
62
|
+
id: '1',
|
|
63
|
+
name: 'John Doe',
|
|
64
|
+
email: 'test@mail.com',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
69
|
+
content: noteText,
|
|
70
|
+
type: 'gpgPassword',
|
|
71
|
+
password: '123',
|
|
72
|
+
format: 'binary',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(encryptedNote).toMatchSnapshot();
|
|
76
|
+
expect(encryptedNoteText).toBeInstanceOf(Uint8Array);
|
|
77
|
+
});
|
|
78
|
+
|
|
45
79
|
test('Should decrypt note content via password', async () => {
|
|
46
80
|
const noteText = `-----BEGIN PGP MESSAGE-----
|
|
47
81
|
|
|
@@ -51,9 +85,9 @@ zt3v0mABvaBJA7corlU8su21TpPApOs/+DMWpVlbI3Zer7QfQK1fSBoSTbCR
|
|
|
51
85
|
5Bjlwt4ZhxFsh+c=
|
|
52
86
|
=csID
|
|
53
87
|
-----END PGP MESSAGE-----`;
|
|
88
|
+
|
|
54
89
|
const note: Note = {
|
|
55
90
|
id: 'id',
|
|
56
|
-
encryptionType: 'gpgPassword',
|
|
57
91
|
meta: {
|
|
58
92
|
title: 'My note title',
|
|
59
93
|
images: [],
|
|
@@ -62,7 +96,8 @@ zt3v0mABvaBJA7corlU8su21TpPApOs/+DMWpVlbI3Zer7QfQK1fSBoSTbCR
|
|
|
62
96
|
},
|
|
63
97
|
};
|
|
64
98
|
|
|
65
|
-
const decryptedNote = await decryptNote(note,
|
|
99
|
+
const decryptedNote = await decryptNote(note, {
|
|
100
|
+
content: noteText,
|
|
66
101
|
type: 'gpgPassword',
|
|
67
102
|
password: '123',
|
|
68
103
|
});
|
|
@@ -70,7 +105,7 @@ zt3v0mABvaBJA7corlU8su21TpPApOs/+DMWpVlbI3Zer7QfQK1fSBoSTbCR
|
|
|
70
105
|
expect(decryptedNote).toMatchSnapshot();
|
|
71
106
|
});
|
|
72
107
|
|
|
73
|
-
test
|
|
108
|
+
test('Should encrypt note via keys', async () => {
|
|
74
109
|
const noteText = `#+ID: qweqwe
|
|
75
110
|
#+TITLE: Hello worlld
|
|
76
111
|
|
|
@@ -78,7 +113,6 @@ test.skip('Should encrypt note via keys', async () => {
|
|
|
78
113
|
|
|
79
114
|
const note: Note = {
|
|
80
115
|
id: 'id',
|
|
81
|
-
encryptionType: 'gpgKeys',
|
|
82
116
|
meta: {
|
|
83
117
|
title: 'My note title for encryption via keys',
|
|
84
118
|
images: [],
|
|
@@ -92,11 +126,13 @@ test.skip('Should encrypt note via keys', async () => {
|
|
|
92
126
|
},
|
|
93
127
|
};
|
|
94
128
|
|
|
95
|
-
const [encryptedNote, encryptedNoteText] = await encryptNote(note,
|
|
129
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
130
|
+
content: noteText,
|
|
96
131
|
type: ModelsPublicNoteEncryptionTypeEnum.GpgKeys,
|
|
97
132
|
publicKey: armoredPublicKey,
|
|
98
133
|
privateKey: armoredPrivateKey,
|
|
99
134
|
privateKeyPassphrase,
|
|
135
|
+
format: 'armored',
|
|
100
136
|
});
|
|
101
137
|
|
|
102
138
|
expect(encryptedNoteText.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
@@ -140,7 +176,6 @@ yEN8xpFUs7A9xryVZOosp9Sfe3IbBkO99sAQ7jV4EoMYk3/GKA==
|
|
|
140
176
|
|
|
141
177
|
const note: Note = {
|
|
142
178
|
id: 'id',
|
|
143
|
-
encryptionType: 'gpgKeys',
|
|
144
179
|
meta: {
|
|
145
180
|
title: 'My note title for decryption via keys',
|
|
146
181
|
images: [],
|
|
@@ -149,7 +184,8 @@ yEN8xpFUs7A9xryVZOosp9Sfe3IbBkO99sAQ7jV4EoMYk3/GKA==
|
|
|
149
184
|
},
|
|
150
185
|
};
|
|
151
186
|
|
|
152
|
-
const decryptedNote = await decryptNote(note,
|
|
187
|
+
const decryptedNote = await decryptNote(note, {
|
|
188
|
+
content: encryptedNoteText,
|
|
153
189
|
type: 'gpgKeys',
|
|
154
190
|
publicKey: armoredPublicKey,
|
|
155
191
|
privateKey: armoredPrivateKey,
|
|
@@ -167,7 +203,6 @@ test('Should not encrypt public note', async () => {
|
|
|
167
203
|
|
|
168
204
|
const note: Note = {
|
|
169
205
|
id: 'id',
|
|
170
|
-
encryptionType: 'gpgPassword',
|
|
171
206
|
meta: {
|
|
172
207
|
title: 'My note title',
|
|
173
208
|
images: [],
|
|
@@ -181,9 +216,11 @@ test('Should not encrypt public note', async () => {
|
|
|
181
216
|
},
|
|
182
217
|
};
|
|
183
218
|
|
|
184
|
-
const [encryptedNote, encryptedNoteText] = await encryptNote(note,
|
|
219
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
220
|
+
content: noteText,
|
|
185
221
|
type: 'gpgPassword',
|
|
186
222
|
password: '123',
|
|
223
|
+
format: 'armored',
|
|
187
224
|
});
|
|
188
225
|
|
|
189
226
|
expect(encryptedNoteText.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
@@ -213,9 +250,11 @@ test('Should encrypt note with empty encrypted property', async () => {
|
|
|
213
250
|
},
|
|
214
251
|
};
|
|
215
252
|
|
|
216
|
-
const [encryptedNote, encryptedNoteText] = await encryptNote(note,
|
|
253
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
254
|
+
content: noteText,
|
|
217
255
|
type: 'gpgPassword',
|
|
218
256
|
password: '123',
|
|
257
|
+
format: 'armored',
|
|
219
258
|
});
|
|
220
259
|
|
|
221
260
|
expect(encryptedNoteText.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
@@ -246,9 +285,11 @@ test('Should not decrypt note without provided encrypted type', async () => {
|
|
|
246
285
|
},
|
|
247
286
|
};
|
|
248
287
|
|
|
249
|
-
const decryptedInfo = await decryptNote(note,
|
|
288
|
+
const decryptedInfo = await decryptNote(note, {
|
|
289
|
+
content: noteText,
|
|
250
290
|
type: 'gpgPassword',
|
|
251
291
|
password: '123',
|
|
292
|
+
format: 'armored',
|
|
252
293
|
});
|
|
253
294
|
|
|
254
295
|
expect(decryptedInfo).toMatchSnapshot();
|
|
@@ -265,7 +306,6 @@ test('Should decrypt note and note meta', async () => {
|
|
|
265
306
|
const note: Note = {
|
|
266
307
|
id: 'id',
|
|
267
308
|
meta: { ...meta },
|
|
268
|
-
encryptionType: 'gpgPassword',
|
|
269
309
|
};
|
|
270
310
|
|
|
271
311
|
const noteText = `#+TITLE: My note title
|
|
@@ -277,19 +317,17 @@ test('Should decrypt note and note meta', async () => {
|
|
|
277
317
|
|
|
278
318
|
Hello world`;
|
|
279
319
|
|
|
280
|
-
const [encryptedNote, encryptedNoteText] = await encryptNote(note,
|
|
320
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
321
|
+
content: noteText,
|
|
281
322
|
type: 'gpgPassword',
|
|
282
323
|
password: '123',
|
|
283
324
|
});
|
|
284
325
|
|
|
285
|
-
const [decryptedNote, decryptedNoteText] = await decryptNote(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
password: '123',
|
|
291
|
-
}
|
|
292
|
-
);
|
|
326
|
+
const [decryptedNote, decryptedNoteText] = await decryptNote(encryptedNote, {
|
|
327
|
+
content: encryptedNoteText,
|
|
328
|
+
type: 'gpgPassword',
|
|
329
|
+
password: '123',
|
|
330
|
+
});
|
|
293
331
|
|
|
294
332
|
expect(decryptedNote.meta).toEqual(meta);
|
|
295
333
|
expect(decryptedNoteText).toEqual(noteText);
|
|
@@ -298,7 +336,6 @@ Hello world`;
|
|
|
298
336
|
test('Should set not encrypted status when params type does not provided', async () => {
|
|
299
337
|
const note: Note = {
|
|
300
338
|
id: 'id',
|
|
301
|
-
encryptionType: 'gpgKeys',
|
|
302
339
|
meta: {
|
|
303
340
|
title: 'My note title for decryption via keys',
|
|
304
341
|
images: [],
|
|
@@ -339,9 +376,50 @@ yEN8xpFUs7A9xryVZOosp9Sfe3IbBkO99sAQ7jV4EoMYk3/GKA==
|
|
|
339
376
|
=LjkG
|
|
340
377
|
-----END PGP MESSAGE-----`;
|
|
341
378
|
|
|
342
|
-
const decryptedNote = await decryptNote(note,
|
|
379
|
+
const decryptedNote = await decryptNote(note, {
|
|
380
|
+
content: noteText,
|
|
343
381
|
type: 'disabled',
|
|
344
382
|
});
|
|
345
383
|
|
|
346
384
|
expect(decryptedNote).toMatchSnapshot();
|
|
347
385
|
});
|
|
386
|
+
|
|
387
|
+
test('Should encrypt note to binary format', async () => {
|
|
388
|
+
const noteText = `#+ID: qweqwe
|
|
389
|
+
#+TITLE: Hello worlld
|
|
390
|
+
|
|
391
|
+
* Hello?`;
|
|
392
|
+
|
|
393
|
+
const note: Note = {
|
|
394
|
+
id: 'id',
|
|
395
|
+
meta: {
|
|
396
|
+
title: 'My note title',
|
|
397
|
+
images: [],
|
|
398
|
+
published: false,
|
|
399
|
+
description: 'Awesome description',
|
|
400
|
+
},
|
|
401
|
+
author: {
|
|
402
|
+
id: '1',
|
|
403
|
+
name: 'John Doe',
|
|
404
|
+
email: 'test@mail.com',
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const [encryptedNote, encryptedNoteText] = await encryptNote(note, {
|
|
409
|
+
content: noteText,
|
|
410
|
+
type: 'gpgPassword',
|
|
411
|
+
password: '123',
|
|
412
|
+
format: 'binary',
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(encryptedNoteText).toBeInstanceOf(Uint8Array);
|
|
416
|
+
expect(encryptedNote).toMatchSnapshot();
|
|
417
|
+
|
|
418
|
+
const [_, decryptedNoteText] = await decryptNote(encryptedNote, {
|
|
419
|
+
content: encryptedNoteText,
|
|
420
|
+
type: 'gpgPassword',
|
|
421
|
+
password: '123',
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
expect(decryptedNoteText).toEqual(noteText);
|
|
425
|
+
});
|
package/encryption/encryption.ts
CHANGED
|
@@ -6,9 +6,16 @@ import {
|
|
|
6
6
|
readKey,
|
|
7
7
|
readMessage,
|
|
8
8
|
readPrivateKey,
|
|
9
|
+
Stream,
|
|
9
10
|
} from 'openpgp';
|
|
10
11
|
import { ModelsPublicNoteEncryptionTypeEnum } from '../remote-api';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
OrgNoteEncryption,
|
|
14
|
+
OrgNotePasswordEncryption,
|
|
15
|
+
WithDecryptionContent,
|
|
16
|
+
} from '../models/encryption';
|
|
17
|
+
import { OrgNoteGpgEncryption, WithEncryptionContent } from 'src/models';
|
|
18
|
+
import { armor as _armor, unarmor as _unarmor, enums } from 'openpgp';
|
|
12
19
|
|
|
13
20
|
export class IncorrectOrMissingPrivateKeyPasswordError extends Error {}
|
|
14
21
|
export class ImpossibleToDecryptWithProvidedKeysError extends Error {}
|
|
@@ -43,74 +50,84 @@ export const encryptViaPassword = withCustomErrors(_encryptViaPassword);
|
|
|
43
50
|
export const decryptViaPassword = withCustomErrors(_decryptViaPassword);
|
|
44
51
|
export const decryptViaKeys = withCustomErrors(_decryptViaKeys);
|
|
45
52
|
|
|
46
|
-
export const encrypt = async
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
export const encrypt = async <
|
|
54
|
+
T extends WithEncryptionContent<OrgNoteEncryption>,
|
|
55
|
+
>(
|
|
56
|
+
encryptionParams: T
|
|
57
|
+
): Promise<T['format'] extends 'binary' ? Uint8Array : string> => {
|
|
50
58
|
if (
|
|
51
59
|
!encryptionParams.type ||
|
|
52
60
|
encryptionParams.type === ModelsPublicNoteEncryptionTypeEnum.Disabled
|
|
53
61
|
) {
|
|
54
|
-
return
|
|
62
|
+
return encryptionParams.content as any;
|
|
55
63
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
|
|
65
|
+
const res = (encryptionParams.type ===
|
|
66
|
+
ModelsPublicNoteEncryptionTypeEnum.GpgKeys
|
|
67
|
+
? await encryptViaKeys(encryptionParams)
|
|
68
|
+
: await encryptViaPassword(encryptionParams)) as unknown as Promise<
|
|
69
|
+
T['format'] extends 'binary' ? Uint8Array : string
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
return res;
|
|
64
73
|
};
|
|
65
74
|
|
|
66
|
-
export const decrypt = async
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
export const decrypt = async <
|
|
76
|
+
T extends WithDecryptionContent<OrgNoteEncryption>,
|
|
77
|
+
>(
|
|
78
|
+
decryptionParams: T
|
|
79
|
+
): Promise<T['format'] extends 'binary' ? Uint8Array : string> => {
|
|
70
80
|
if (
|
|
71
|
-
!
|
|
72
|
-
|
|
81
|
+
!decryptionParams.type ||
|
|
82
|
+
decryptionParams.type === ModelsPublicNoteEncryptionTypeEnum.Disabled
|
|
73
83
|
) {
|
|
74
|
-
return
|
|
84
|
+
return decryptionParams.content as any;
|
|
75
85
|
}
|
|
76
|
-
const decryptedNote =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
: await decryptViaPassword(text, encryptionParams.password);
|
|
86
|
+
const decryptedNote = (decryptionParams.type ===
|
|
87
|
+
ModelsPublicNoteEncryptionTypeEnum.GpgKeys
|
|
88
|
+
? await decryptViaKeys(decryptionParams)
|
|
89
|
+
: await decryptViaPassword(decryptionParams)) as unknown as Promise<
|
|
90
|
+
T['format'] extends 'binary' ? Uint8Array : string
|
|
91
|
+
>;
|
|
92
|
+
|
|
84
93
|
return decryptedNote;
|
|
85
94
|
};
|
|
86
95
|
|
|
87
|
-
async function _encryptViaPassword
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
async function _encryptViaPassword<
|
|
97
|
+
T extends WithEncryptionContent<OrgNotePasswordEncryption>,
|
|
98
|
+
>({
|
|
99
|
+
content,
|
|
100
|
+
password,
|
|
101
|
+
format = 'binary',
|
|
102
|
+
}: T): Promise<T['format'] extends 'binary' ? Uint8Array : string> {
|
|
91
103
|
const message = await createMessage({
|
|
92
|
-
text,
|
|
104
|
+
text: content,
|
|
93
105
|
});
|
|
94
106
|
|
|
95
107
|
const encryptedMessage = await _encrypt({
|
|
96
108
|
message,
|
|
97
|
-
format:
|
|
109
|
+
format: format as any,
|
|
98
110
|
passwords: [password],
|
|
99
111
|
});
|
|
100
112
|
|
|
101
|
-
return encryptedMessage
|
|
113
|
+
return encryptedMessage as Promise<
|
|
114
|
+
T['format'] extends 'binary' ? Uint8Array : string
|
|
115
|
+
>;
|
|
102
116
|
}
|
|
103
117
|
|
|
104
|
-
async function _encryptViaKeys
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
export async function _encryptViaKeys<
|
|
119
|
+
T extends WithEncryptionContent<OrgNoteGpgEncryption>,
|
|
120
|
+
>({
|
|
121
|
+
content,
|
|
122
|
+
publicKey: armoredPublicKey,
|
|
123
|
+
privateKey: armoredPrivateKey,
|
|
124
|
+
privateKeyPassphrase,
|
|
125
|
+
format = 'binary',
|
|
126
|
+
}: T): Promise<T['format'] extends 'binary' ? Uint8Array : string> {
|
|
110
127
|
const publicKey = await readKey({ armoredKey: armoredPublicKey });
|
|
111
128
|
|
|
112
129
|
const message = await createMessage({
|
|
113
|
-
text,
|
|
130
|
+
text: content,
|
|
114
131
|
});
|
|
115
132
|
|
|
116
133
|
const encryptedPrivateKey = await readPrivateKey({
|
|
@@ -126,52 +143,73 @@ async function _encryptViaKeys(
|
|
|
126
143
|
|
|
127
144
|
const encryptedMessage = await _encrypt({
|
|
128
145
|
message,
|
|
129
|
-
format:
|
|
146
|
+
format: format as any,
|
|
130
147
|
encryptionKeys: publicKey,
|
|
131
148
|
signingKeys: privateKey,
|
|
132
149
|
});
|
|
133
150
|
|
|
134
|
-
return encryptedMessage
|
|
151
|
+
return encryptedMessage as Promise<
|
|
152
|
+
T['format'] extends 'binary' ? Uint8Array : string
|
|
153
|
+
>;
|
|
135
154
|
}
|
|
136
155
|
|
|
137
|
-
async function _decryptViaPassword
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
156
|
+
async function _decryptViaPassword<
|
|
157
|
+
T extends Omit<WithDecryptionContent<OrgNotePasswordEncryption>, 'type'>,
|
|
158
|
+
>({
|
|
159
|
+
content,
|
|
160
|
+
password,
|
|
161
|
+
format = 'utf8',
|
|
162
|
+
}: T): Promise<T['format'] extends 'binary' ? Uint8Array : string> {
|
|
163
|
+
const isArmoredContent = typeof content === 'string';
|
|
164
|
+
|
|
165
|
+
const message = await (isArmoredContent
|
|
166
|
+
? readMessage({ armoredMessage: content })
|
|
167
|
+
: readMessage({ binaryMessage: content }));
|
|
142
168
|
|
|
143
169
|
const { data: decryptedText } = await _decrypt({
|
|
144
170
|
message,
|
|
171
|
+
format,
|
|
145
172
|
passwords: password,
|
|
146
173
|
});
|
|
147
174
|
|
|
148
|
-
return decryptedText
|
|
175
|
+
return decryptedText as Promise<
|
|
176
|
+
T['format'] extends 'binary' ? Uint8Array : string
|
|
177
|
+
>;
|
|
149
178
|
}
|
|
150
179
|
|
|
151
|
-
async function _decryptViaKeys
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
180
|
+
async function _decryptViaKeys<
|
|
181
|
+
T extends Omit<WithDecryptionContent<OrgNoteGpgEncryption>, 'type'>,
|
|
182
|
+
>({
|
|
183
|
+
privateKey: armoredPrivateKey,
|
|
184
|
+
privateKeyPassphrase,
|
|
185
|
+
content,
|
|
186
|
+
format = 'utf8',
|
|
187
|
+
}: T): Promise<T['format'] extends 'binary' ? Uint8Array : string> {
|
|
156
188
|
const encryptedPrivateKey = await readPrivateKey({
|
|
157
189
|
armoredKey: armoredPrivateKey,
|
|
158
190
|
});
|
|
159
191
|
|
|
160
|
-
const privateKey =
|
|
192
|
+
const privateKey = privateKeyPassphrase
|
|
161
193
|
? await decryptKey({
|
|
162
194
|
privateKey: encryptedPrivateKey,
|
|
163
|
-
passphrase:
|
|
195
|
+
passphrase: privateKeyPassphrase,
|
|
164
196
|
})
|
|
165
197
|
: encryptedPrivateKey;
|
|
166
198
|
|
|
167
|
-
const
|
|
199
|
+
const isString = typeof content === 'string';
|
|
200
|
+
const message = await (isString
|
|
201
|
+
? readMessage({ armoredMessage: content })
|
|
202
|
+
: readMessage({ binaryMessage: content }));
|
|
168
203
|
|
|
169
204
|
const { data: decryptedText } = await _decrypt({
|
|
170
205
|
message,
|
|
206
|
+
format,
|
|
171
207
|
decryptionKeys: privateKey,
|
|
172
208
|
});
|
|
173
209
|
|
|
174
|
-
return decryptedText
|
|
210
|
+
return decryptedText as Promise<
|
|
211
|
+
T['format'] extends 'binary' ? Uint8Array : string
|
|
212
|
+
>;
|
|
175
213
|
}
|
|
176
214
|
|
|
177
215
|
function withCustomErrors<P extends unknown[], T>(
|
|
@@ -213,3 +251,14 @@ function withCustomErrors<P extends unknown[], T>(
|
|
|
213
251
|
}
|
|
214
252
|
};
|
|
215
253
|
}
|
|
254
|
+
|
|
255
|
+
// TODO: feat/native-encryption-support add custom error handling
|
|
256
|
+
export function armor(data: Uint8Array): string {
|
|
257
|
+
return _armor(enums.armor.message, data);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export async function unarmor(
|
|
261
|
+
data: string
|
|
262
|
+
): Promise<{ text: string; data: Stream<Uint8Array>; type: enums.armor }> {
|
|
263
|
+
return await _unarmor(data);
|
|
264
|
+
}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ModelsPublicNote,
|
|
3
|
-
ModelsPublicNoteEncryptionTypeEnum,
|
|
4
|
-
} from '../remote-api';
|
|
1
|
+
import { ModelsPublicNoteEncryptionTypeEnum } from '../remote-api';
|
|
5
2
|
|
|
3
|
+
import { decrypt, encrypt } from './encryption';
|
|
6
4
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '
|
|
12
|
-
import { OrgNoteEncryption } from '../models/encryption';
|
|
5
|
+
OrgNoteEncryption,
|
|
6
|
+
WithDecryptionContent,
|
|
7
|
+
WithEncryptionContent,
|
|
8
|
+
WithNoteDecryptionContent,
|
|
9
|
+
} from '../models/encryption';
|
|
13
10
|
import { parse, withMetaInfo } from 'org-mode-ast';
|
|
14
11
|
import { isGpgEncrypted } from '..';
|
|
15
12
|
|
|
16
|
-
export type NoteEncryptionType = `${ModelsPublicNote['encryptionType']}`;
|
|
17
|
-
|
|
18
13
|
export interface AbstractEncryptedNote {
|
|
19
|
-
encryptionType?: NoteEncryptionType;
|
|
20
14
|
encrypted?: boolean;
|
|
21
15
|
meta: {
|
|
22
16
|
[key: string]: any;
|
|
@@ -29,8 +23,7 @@ export type EncryptionResult<T> = Promise<[T, string]>;
|
|
|
29
23
|
// TODO: master change signature for encrypt notes without content
|
|
30
24
|
export async function encryptNote<T extends AbstractEncryptedNote>(
|
|
31
25
|
note: T,
|
|
32
|
-
|
|
33
|
-
encryptionParams: OrgNoteEncryption
|
|
26
|
+
encryptionParams: WithEncryptionContent<OrgNoteEncryption>
|
|
34
27
|
): EncryptionResult<T> {
|
|
35
28
|
note.encrypted = false;
|
|
36
29
|
if (
|
|
@@ -38,132 +31,47 @@ export async function encryptNote<T extends AbstractEncryptedNote>(
|
|
|
38
31
|
encryptionParams.type === ModelsPublicNoteEncryptionTypeEnum.Disabled ||
|
|
39
32
|
note.meta.published
|
|
40
33
|
) {
|
|
41
|
-
return [note,
|
|
34
|
+
return [note, encryptionParams.content];
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
note.meta = { id: note.meta.id, published: note.meta.published };
|
|
45
38
|
|
|
46
|
-
const
|
|
47
|
-
encryptionParams.type === ModelsPublicNoteEncryptionTypeEnum.GpgKeys
|
|
48
|
-
? await encryptNoteViaKeys(
|
|
49
|
-
note,
|
|
50
|
-
encryptionParams.publicKey,
|
|
51
|
-
encryptionParams.privateKey,
|
|
52
|
-
encryptionParams.privateKeyPassphrase
|
|
53
|
-
)
|
|
54
|
-
: await encryptNoteViaPassword(note, noteText, encryptionParams.password);
|
|
39
|
+
const encryptedContent = await encrypt(encryptionParams);
|
|
55
40
|
|
|
56
|
-
encryptedNote
|
|
57
|
-
return [encryptedNote, encryptedNoteText];
|
|
58
|
-
}
|
|
59
|
-
export async function encryptNoteViaPassword<T extends AbstractEncryptedNote>(
|
|
60
|
-
note: T,
|
|
61
|
-
noteText: string,
|
|
62
|
-
password: string
|
|
63
|
-
): Promise<[T, string]> {
|
|
64
|
-
const encryptedNoteText = await encryptViaPassword(noteText, password);
|
|
65
|
-
return [
|
|
66
|
-
{
|
|
67
|
-
...note,
|
|
68
|
-
encrypted: ModelsPublicNoteEncryptionTypeEnum.GpgPassword,
|
|
69
|
-
},
|
|
70
|
-
encryptedNoteText,
|
|
71
|
-
];
|
|
72
|
-
}
|
|
41
|
+
const encryptedNote = { ...note, encrypted: true };
|
|
73
42
|
|
|
74
|
-
|
|
75
|
-
note: T,
|
|
76
|
-
noteText: string,
|
|
77
|
-
publicKey: string,
|
|
78
|
-
privateKey: string,
|
|
79
|
-
privateKeyPassphrase?: string
|
|
80
|
-
): Promise<[T, string]> {
|
|
81
|
-
const encryptedNoteText = await encryptViaKeys(
|
|
82
|
-
noteText,
|
|
83
|
-
publicKey,
|
|
84
|
-
privateKey,
|
|
85
|
-
privateKeyPassphrase
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return [
|
|
89
|
-
{
|
|
90
|
-
...note,
|
|
91
|
-
encrypted: ModelsPublicNoteEncryptionTypeEnum.GpgKeys,
|
|
92
|
-
},
|
|
93
|
-
encryptedNoteText,
|
|
94
|
-
];
|
|
43
|
+
return [encryptedNote, encryptedContent];
|
|
95
44
|
}
|
|
96
45
|
|
|
97
46
|
export async function decryptNote<T extends AbstractEncryptedNote>(
|
|
98
47
|
note: T,
|
|
99
|
-
|
|
100
|
-
encryptionParams: OrgNoteEncryption
|
|
48
|
+
encryptionParams: WithNoteDecryptionContent<OrgNoteEncryption>
|
|
101
49
|
): EncryptionResult<T> {
|
|
102
|
-
const isContentEncrypted = isGpgEncrypted(
|
|
50
|
+
const isContentEncrypted = isGpgEncrypted(encryptionParams.content);
|
|
103
51
|
if (
|
|
104
52
|
note.meta.published ||
|
|
105
|
-
!note.encryptionType ||
|
|
106
53
|
!encryptionParams.type ||
|
|
107
54
|
encryptionParams.type === ModelsPublicNoteEncryptionTypeEnum.Disabled ||
|
|
108
55
|
!isContentEncrypted
|
|
109
56
|
) {
|
|
110
57
|
note.encrypted = isContentEncrypted;
|
|
111
|
-
return [note,
|
|
58
|
+
return [note, encryptionParams.content];
|
|
112
59
|
}
|
|
113
60
|
note.encrypted = true;
|
|
114
|
-
const
|
|
115
|
-
encryptionParams
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
noteText,
|
|
119
|
-
encryptionParams.privateKey,
|
|
120
|
-
encryptionParams.privateKeyPassphrase
|
|
121
|
-
)
|
|
122
|
-
: await decryptNoteViaPassword(note, noteText, encryptionParams.password);
|
|
61
|
+
const decryptionParams = {
|
|
62
|
+
...encryptionParams,
|
|
63
|
+
format: 'utf8',
|
|
64
|
+
} as WithDecryptionContent<OrgNoteEncryption>;
|
|
123
65
|
|
|
66
|
+
const decryptedNoteText = await decrypt(decryptionParams);
|
|
124
67
|
const parsed = withMetaInfo(parse(decryptedNoteText));
|
|
125
68
|
|
|
126
69
|
return [
|
|
127
70
|
{
|
|
128
|
-
...
|
|
71
|
+
...note,
|
|
129
72
|
encrypted: false,
|
|
130
73
|
meta: parsed.meta,
|
|
131
74
|
},
|
|
132
75
|
decryptedNoteText,
|
|
133
76
|
];
|
|
134
77
|
}
|
|
135
|
-
|
|
136
|
-
export async function decryptNoteViaPassword<T extends AbstractEncryptedNote>(
|
|
137
|
-
note: T,
|
|
138
|
-
noteText: string,
|
|
139
|
-
password: string
|
|
140
|
-
): EncryptionResult<T> {
|
|
141
|
-
const decryptedNoteText = await decryptViaPassword(noteText, password);
|
|
142
|
-
return [
|
|
143
|
-
{
|
|
144
|
-
...note,
|
|
145
|
-
encrypted: ModelsPublicNoteEncryptionTypeEnum.GpgPassword,
|
|
146
|
-
},
|
|
147
|
-
decryptedNoteText,
|
|
148
|
-
];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export async function decryptNoteViaKeys<T extends AbstractEncryptedNote>(
|
|
152
|
-
note: T,
|
|
153
|
-
noteText: string,
|
|
154
|
-
privateKey: string,
|
|
155
|
-
privateKeyPassphrase?: string
|
|
156
|
-
): EncryptionResult<T> {
|
|
157
|
-
const decryptedNoteText = await decryptViaKeys(
|
|
158
|
-
noteText,
|
|
159
|
-
privateKey,
|
|
160
|
-
privateKeyPassphrase
|
|
161
|
-
);
|
|
162
|
-
return [
|
|
163
|
-
{
|
|
164
|
-
...note,
|
|
165
|
-
encrypted: ModelsPublicNoteEncryptionTypeEnum.GpgKeys,
|
|
166
|
-
},
|
|
167
|
-
decryptedNoteText,
|
|
168
|
-
];
|
|
169
|
-
}
|
package/models/encryption.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import type { ModelsPublicNoteEncryptionTypeEnum } from '../remote-api';
|
|
2
2
|
|
|
3
|
+
export type EcnryptionFormat = 'binary' | 'armored';
|
|
4
|
+
|
|
5
|
+
export interface BaseOrgNoteEncryption {
|
|
6
|
+
format?: 'binary' | 'armored';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface BaseOrgNoteDecryption {
|
|
10
|
+
format?: 'utf8' | 'binary';
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
export interface OrgNoteGpgEncryption {
|
|
4
14
|
type: typeof ModelsPublicNoteEncryptionTypeEnum.GpgKeys;
|
|
15
|
+
/* Armored private key */
|
|
5
16
|
privateKey: string;
|
|
17
|
+
/* Armored public key */
|
|
6
18
|
publicKey: string;
|
|
7
19
|
privateKeyPassphrase?: string;
|
|
8
20
|
}
|
|
@@ -20,3 +32,19 @@ export type OrgNoteEncryption =
|
|
|
20
32
|
| OrgNoteGpgEncryption
|
|
21
33
|
| OrgNotePasswordEncryption
|
|
22
34
|
| OrgNoteDisabledEncryption;
|
|
35
|
+
|
|
36
|
+
export type EncryptionData = {
|
|
37
|
+
content: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type WithEncryptionContent<
|
|
41
|
+
T extends OrgNoteEncryption = OrgNoteEncryption,
|
|
42
|
+
> = T & EncryptionData & BaseOrgNoteEncryption;
|
|
43
|
+
|
|
44
|
+
export type WithDecryptionContent<
|
|
45
|
+
T extends OrgNoteEncryption = OrgNoteEncryption,
|
|
46
|
+
> = T & EncryptionData & BaseOrgNoteDecryption;
|
|
47
|
+
|
|
48
|
+
export type WithNoteDecryptionContent<
|
|
49
|
+
T extends OrgNoteEncryption = OrgNoteEncryption,
|
|
50
|
+
> = T & EncryptionData;
|
package/package.json
CHANGED