orgnote-api 0.7.1 → 0.7.3
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/encryption.ts +152 -0
- package/encryption/index.ts +2 -0
- package/encryption/note-encryption.ts +133 -0
- package/package.json +8 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMessage,
|
|
3
|
+
decrypt,
|
|
4
|
+
decryptKey,
|
|
5
|
+
encrypt,
|
|
6
|
+
readKey,
|
|
7
|
+
readMessage,
|
|
8
|
+
readPrivateKey,
|
|
9
|
+
} from 'openpgp';
|
|
10
|
+
|
|
11
|
+
export class IncorrectOrMissingPrivateKeyPasswordError extends Error {}
|
|
12
|
+
export class ImpossibleToDecryptWithProvidedKeysError extends Error {}
|
|
13
|
+
export class IncorrectEncryptionPasswordError extends Error {}
|
|
14
|
+
|
|
15
|
+
const noPrivateKeyPassphraseProvidedErrorMsg =
|
|
16
|
+
'Error: Signing key is not decrypted.';
|
|
17
|
+
const incorrectPrivateKeyPassphraseErrorMsg =
|
|
18
|
+
'Error decrypting private key: Incorrect key passphrase';
|
|
19
|
+
const decryptionKeyIsNotDecryptedErrorMsg =
|
|
20
|
+
'Error decrypting message: Decryption key is not decrypted.';
|
|
21
|
+
const corruptedPrivateKeyErrorMsg = 'Misformed armored text';
|
|
22
|
+
|
|
23
|
+
const decriptionFailedErrorMsg =
|
|
24
|
+
'Error decrypting message: Session key decryption failed.';
|
|
25
|
+
const incorrectEncryptionPasswordErrorMsg =
|
|
26
|
+
'Error decrypting message: Modification detected.';
|
|
27
|
+
|
|
28
|
+
export const encryptViaKeys = withCustomErrors(_encryptViaKeys);
|
|
29
|
+
export const encryptViaPassword = withCustomErrors(_encryptViaPassword);
|
|
30
|
+
export const decryptViaPassword = withCustomErrors(_decryptViaPassword);
|
|
31
|
+
export const decryptViaKeys = withCustomErrors(_decryptViaKeys);
|
|
32
|
+
|
|
33
|
+
async function _encryptViaPassword(
|
|
34
|
+
text: string,
|
|
35
|
+
password: string
|
|
36
|
+
): Promise<string> {
|
|
37
|
+
const message = await createMessage({
|
|
38
|
+
text,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const encryptedMessage = await encrypt({
|
|
42
|
+
message,
|
|
43
|
+
format: 'armored',
|
|
44
|
+
passwords: [password],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return encryptedMessage.toString();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function _encryptViaKeys(
|
|
51
|
+
text: string,
|
|
52
|
+
armoredPublicKey: string,
|
|
53
|
+
armoredPrivateKey: string,
|
|
54
|
+
privateKeyPassphrase?: string
|
|
55
|
+
): Promise<string> {
|
|
56
|
+
const publicKey = await readKey({ armoredKey: armoredPublicKey });
|
|
57
|
+
|
|
58
|
+
const message = await createMessage({
|
|
59
|
+
text,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const encryptedPrivateKey = await readPrivateKey({
|
|
63
|
+
armoredKey: armoredPrivateKey,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const privateKey = privateKeyPassphrase
|
|
67
|
+
? await decryptKey({
|
|
68
|
+
privateKey: encryptedPrivateKey,
|
|
69
|
+
passphrase: privateKeyPassphrase,
|
|
70
|
+
})
|
|
71
|
+
: encryptedPrivateKey;
|
|
72
|
+
|
|
73
|
+
const encryptedMessage = await encrypt({
|
|
74
|
+
message,
|
|
75
|
+
format: 'armored',
|
|
76
|
+
encryptionKeys: publicKey,
|
|
77
|
+
signingKeys: privateKey,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return encryptedMessage.toString();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function _decryptViaPassword(
|
|
84
|
+
data: string,
|
|
85
|
+
password: string
|
|
86
|
+
): Promise<string> {
|
|
87
|
+
const message = await readMessage({ armoredMessage: data });
|
|
88
|
+
|
|
89
|
+
const { data: decryptedText } = await decrypt({
|
|
90
|
+
message,
|
|
91
|
+
passwords: password,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return decryptedText.toString();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function _decryptViaKeys(
|
|
98
|
+
data: string,
|
|
99
|
+
armoredPrivateKey: string,
|
|
100
|
+
privateKeyPassword?: string
|
|
101
|
+
): Promise<string> {
|
|
102
|
+
const encryptedPrivateKey = await readPrivateKey({
|
|
103
|
+
armoredKey: armoredPrivateKey,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const privateKey = privateKeyPassword
|
|
107
|
+
? await decryptKey({
|
|
108
|
+
privateKey: encryptedPrivateKey,
|
|
109
|
+
passphrase: privateKeyPassword,
|
|
110
|
+
})
|
|
111
|
+
: encryptedPrivateKey;
|
|
112
|
+
|
|
113
|
+
const message = await readMessage({ armoredMessage: data });
|
|
114
|
+
|
|
115
|
+
const { data: decryptedText } = await decrypt({
|
|
116
|
+
message,
|
|
117
|
+
decryptionKeys: privateKey,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return decryptedText.toString();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function withCustomErrors<P extends unknown[], T>(
|
|
124
|
+
fn: (...args: P) => Promise<T | never>
|
|
125
|
+
) {
|
|
126
|
+
return async (...args: P): Promise<T> => {
|
|
127
|
+
try {
|
|
128
|
+
return await fn(...args);
|
|
129
|
+
} catch (e: unknown) {
|
|
130
|
+
if (!(e instanceof Error)) {
|
|
131
|
+
throw e;
|
|
132
|
+
}
|
|
133
|
+
if (
|
|
134
|
+
[
|
|
135
|
+
noPrivateKeyPassphraseProvidedErrorMsg,
|
|
136
|
+
incorrectPrivateKeyPassphraseErrorMsg,
|
|
137
|
+
corruptedPrivateKeyErrorMsg,
|
|
138
|
+
decryptionKeyIsNotDecryptedErrorMsg,
|
|
139
|
+
].includes(e.message)
|
|
140
|
+
) {
|
|
141
|
+
throw new IncorrectOrMissingPrivateKeyPasswordError(e.message);
|
|
142
|
+
}
|
|
143
|
+
if (e.message === decriptionFailedErrorMsg) {
|
|
144
|
+
throw new ImpossibleToDecryptWithProvidedKeysError(e.message);
|
|
145
|
+
}
|
|
146
|
+
if (e.message === incorrectEncryptionPasswordErrorMsg) {
|
|
147
|
+
throw new IncorrectEncryptionPasswordError();
|
|
148
|
+
}
|
|
149
|
+
throw e;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ModelsPublicNote,
|
|
3
|
+
ModelsPublicNoteEncryptedEnum,
|
|
4
|
+
} from 'src/remote-api';
|
|
5
|
+
import {
|
|
6
|
+
decryptViaKeys,
|
|
7
|
+
decryptViaPassword,
|
|
8
|
+
encryptViaKeys,
|
|
9
|
+
encryptViaPassword,
|
|
10
|
+
} from './encryption';
|
|
11
|
+
import { OrgNoteEncryption } from 'src/models/encryption';
|
|
12
|
+
import { parse, withMetaInfo } from 'org-mode-ast';
|
|
13
|
+
|
|
14
|
+
export interface AbstractEncryptedNote {
|
|
15
|
+
content: string;
|
|
16
|
+
encrypted?: ModelsPublicNote['encrypted'];
|
|
17
|
+
meta: {
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
published: boolean;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function encryptNote<T extends AbstractEncryptedNote>(
|
|
24
|
+
note: T,
|
|
25
|
+
encryptionParams: OrgNoteEncryption
|
|
26
|
+
): Promise<T> {
|
|
27
|
+
if (
|
|
28
|
+
!encryptionParams.type ||
|
|
29
|
+
encryptionParams.type === ModelsPublicNoteEncryptedEnum.Disabled ||
|
|
30
|
+
note.meta.published
|
|
31
|
+
) {
|
|
32
|
+
return note;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
note.meta = { id: note.meta.id, published: note.meta.published };
|
|
36
|
+
|
|
37
|
+
if (encryptionParams.type === ModelsPublicNoteEncryptedEnum.GpgKeys) {
|
|
38
|
+
return encryptNoteViaKeys(
|
|
39
|
+
note,
|
|
40
|
+
encryptionParams.publicKey,
|
|
41
|
+
encryptionParams.privateKey,
|
|
42
|
+
encryptionParams.privateKeyPassphrase
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return encryptNoteViaPassword(note, encryptionParams.password);
|
|
46
|
+
}
|
|
47
|
+
export async function encryptNoteViaPassword<T extends AbstractEncryptedNote>(
|
|
48
|
+
note: T,
|
|
49
|
+
password: string
|
|
50
|
+
): Promise<T> {
|
|
51
|
+
const content = encryptViaPassword(note.content, password);
|
|
52
|
+
return {
|
|
53
|
+
...note,
|
|
54
|
+
content,
|
|
55
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgPassword,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function encryptNoteViaKeys<T extends AbstractEncryptedNote>(
|
|
60
|
+
note: T,
|
|
61
|
+
publicKey: string,
|
|
62
|
+
privateKey: string,
|
|
63
|
+
privateKeyPassphrase?: string
|
|
64
|
+
): Promise<T> {
|
|
65
|
+
const content = await encryptViaKeys(
|
|
66
|
+
note.content,
|
|
67
|
+
publicKey,
|
|
68
|
+
privateKey,
|
|
69
|
+
privateKeyPassphrase
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
...note,
|
|
74
|
+
content,
|
|
75
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgKeys,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function decryptNote<T extends AbstractEncryptedNote>(
|
|
80
|
+
note: T,
|
|
81
|
+
encryptionParams: OrgNoteEncryption
|
|
82
|
+
): Promise<T> {
|
|
83
|
+
if (
|
|
84
|
+
!encryptionParams.type ||
|
|
85
|
+
encryptionParams.type === ModelsPublicNoteEncryptedEnum.Disabled
|
|
86
|
+
) {
|
|
87
|
+
return note;
|
|
88
|
+
}
|
|
89
|
+
const decryptedNote =
|
|
90
|
+
encryptionParams.type === ModelsPublicNoteEncryptedEnum.GpgKeys
|
|
91
|
+
? await decryptNoteViaKeys(
|
|
92
|
+
note,
|
|
93
|
+
encryptionParams.privateKey,
|
|
94
|
+
encryptionParams.privateKeyPassphrase
|
|
95
|
+
)
|
|
96
|
+
: await decryptNoteViaPassword(note, encryptionParams.password);
|
|
97
|
+
|
|
98
|
+
const parsed = withMetaInfo(parse(decryptedNote.content));
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...decryptedNote,
|
|
102
|
+
meta: { ...decryptedNote.meta, published: parsed.meta.published },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function decryptNoteViaPassword<T extends AbstractEncryptedNote>(
|
|
107
|
+
note: T,
|
|
108
|
+
password: string
|
|
109
|
+
): Promise<T> {
|
|
110
|
+
const content = decryptViaPassword(note.content, password);
|
|
111
|
+
return {
|
|
112
|
+
...note,
|
|
113
|
+
content,
|
|
114
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgPassword,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function decryptNoteViaKeys<T extends AbstractEncryptedNote>(
|
|
119
|
+
note: T,
|
|
120
|
+
privateKey: string,
|
|
121
|
+
privateKeyPassphrase?: string
|
|
122
|
+
): Promise<T> {
|
|
123
|
+
const content = await decryptViaKeys(
|
|
124
|
+
note.content,
|
|
125
|
+
privateKey,
|
|
126
|
+
privateKeyPassphrase
|
|
127
|
+
);
|
|
128
|
+
return {
|
|
129
|
+
...note,
|
|
130
|
+
content,
|
|
131
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgKeys,
|
|
132
|
+
};
|
|
133
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orgnote-api",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Official API for creating extensions for OrgNote app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -17,7 +17,13 @@
|
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "git+https://github.com/artawower/orgnote-api.git"
|
|
19
19
|
},
|
|
20
|
-
"files": [
|
|
20
|
+
"files": [
|
|
21
|
+
"index.ts",
|
|
22
|
+
"api.ts",
|
|
23
|
+
"remote-api/**",
|
|
24
|
+
"models/**",
|
|
25
|
+
"encryption/**"
|
|
26
|
+
],
|
|
21
27
|
"exports": {
|
|
22
28
|
".": "./index.ts",
|
|
23
29
|
"./remote-api": "./remote-api/index.ts",
|