Skip to content

Commit f784e6d

Browse files
committed
Fix Permanent IV / IV reuse #11
1 parent cd8fa85 commit f784e6d

10 files changed

+198
-89
lines changed

Diff for: README.md

+1-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Under the hood it uses the Advanced Encryption Standard (AES) in GCM mode.
1010

1111
> WARNING: Use at your own risk.
1212
> - Your passwords are never stored anywhere, if you forget your password you can't decrypt your text.
13-
> - There havn't been any audits for the soundness of encryption methods being used. Unwanted decyption by a 3rd party may still be possible if they have access to your files.
13+
> - There haven't been any audits for the soundness of encryption methods being used. Unwanted decryption by a 3rd party may still be possible if they have access to your files.
1414
1515
## Usage
1616

@@ -65,6 +65,3 @@ You can install the plugin via the Community Plugins tab within Obsidian by sear
6565
<a href="https://www.buymeacoffee.com/cleon"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=cleon&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff"></a>
6666

6767
Thank you for your support 🙏
68-
69-
## More information
70-
- [Encryption details](https://github.com/meld-cp/obsidian-encrypt/blob/main/docs/crypto-details.md)

Diff for: docs/crypto-details.md

-33
This file was deleted.

Diff for: manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"id": "meld-encrypt",
33
"name": "Meld Encrypt",
4-
"version": "1.3.4",
5-
"minAppVersion": "0.12.12",
4+
"version": "1.4.0",
5+
"minAppVersion": "0.12.15",
66
"description": "Hide secrets in your notes",
77
"author": "meld-cp",
88
"authorUrl": "https://github.com/meld-cp/obsidian-encrypt",

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"obsidian": "https://github.com/obsidianmd/obsidian-api.git",
1919
"rollup": "^2.32.1",
2020
"rollup-plugin-copy": "^3.4.0",
21-
"tslib": "^2.0.3",
21+
"tslib": "^2.3.1",
2222
"typescript": "^4.0.3"
2323
}
2424
}

Diff for: rollup.config.js

-7
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,7 @@ export default {
2929
src: 'dist/*',
3030
dest: 'test-vault/.obsidian/plugins/meld-encrypt/'
3131
},
32-
// { src: 'assets/images/**/*', dest: 'dist/public/images' }
3332
]
34-
// assets: [
35-
// 'dest/main.js',
36-
// 'manifest.json',
37-
// 'src/style.css',
38-
// ],
39-
// outputDir: 'test-vault/.obsidian/plugins/meld-encrypt'
4033
})
4134
],
4235

Diff for: src/CryptoHelper.ts

+99-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,105 @@
1+
const vectorSize = 16;
2+
const utf8Encoder = new TextEncoder();
3+
const utf8Decoder = new TextDecoder();
4+
const iterations = 1000;
5+
const salt = utf8Encoder.encode(navigator.userAgent);
6+
7+
export class CryptoHelperV2 {
8+
9+
private async deriveKey(password:string) :Promise<CryptoKey> {
10+
const buffer = utf8Encoder.encode(password);
11+
const key = await crypto.subtle.importKey('raw', buffer, {name: 'PBKDF2'}, false, ['deriveKey']);
12+
const privateKey = crypto.subtle.deriveKey(
13+
{
14+
name: 'PBKDF2',
15+
hash: {name: 'SHA-256'},
16+
iterations,
17+
salt
18+
},
19+
key,
20+
{
21+
name: 'AES-GCM',
22+
length: 256
23+
},
24+
false,
25+
['encrypt', 'decrypt']
26+
);
27+
28+
return privateKey;
29+
}
30+
31+
public async encryptToBase64(text: string, password: string): Promise<string> {
32+
33+
const key = await this.deriveKey(password);
34+
35+
const textBytesToEncrypt = utf8Encoder.encode(text);
36+
const vector = crypto.getRandomValues(new Uint8Array(vectorSize));
37+
38+
// encrypt into bytes
39+
const encryptedBytes = new Uint8Array(
40+
await crypto.subtle.encrypt(
41+
{name: 'AES-GCM', iv: vector},
42+
key,
43+
textBytesToEncrypt
44+
)
45+
);
46+
47+
const finalBytes = new Uint8Array( vector.byteLength + encryptedBytes.byteLength );
48+
finalBytes.set( vector, 0 );
49+
finalBytes.set( encryptedBytes, vector.byteLength );
50+
51+
//convert array to base64
52+
const base64Text = btoa( String.fromCharCode(...finalBytes) );
53+
54+
return base64Text;
55+
}
56+
57+
private stringToArray(str: string): Uint8Array {
58+
var result = [];
59+
for (var i = 0; i < str.length; i++) {
60+
result.push(str.charCodeAt(i));
61+
}
62+
return new Uint8Array(result);
63+
}
64+
65+
public async decryptFromBase64(base64Encoded: string, password: string): Promise<string> {
66+
try {
67+
68+
let bytesToDecode = this.stringToArray(atob(base64Encoded));
69+
70+
// extract iv
71+
const vector = bytesToDecode.slice(0,vectorSize);
72+
73+
// extract encrypted text
74+
const encryptedTextBytes = bytesToDecode.slice(vectorSize);
75+
76+
const key = await this.deriveKey(password);
77+
78+
// decrypt into bytes
79+
let decryptedBytes = await crypto.subtle.decrypt(
80+
{name: 'AES-GCM', iv: vector},
81+
key,
82+
encryptedTextBytes
83+
);
84+
85+
// convert bytes to text
86+
let decryptedText = utf8Decoder.decode(decryptedBytes);
87+
return decryptedText;
88+
} catch (e) {
89+
//console.error(e);
90+
return null;
91+
}
92+
}
93+
94+
}
195

2-
const algorithm = {
96+
const algorithmObsolete = {
397
name: 'AES-GCM',
498
iv: new Uint8Array([196, 190, 240, 190, 188, 78, 41, 132, 15, 220, 84, 211]),
599
tagLength: 128
6100
}
7101

8-
export default class CryptoHelper {
102+
export class CryptoHelperObsolete {
9103

10104
private async buildKey(password: string) {
11105
let utf8Encode = new TextEncoder();
@@ -16,7 +110,7 @@ export default class CryptoHelper {
16110
let key = await crypto.subtle.importKey(
17111
'raw',
18112
passwordDigest,
19-
algorithm,
113+
algorithmObsolete,
20114
false,
21115
['encrypt', 'decrypt']
22116
);
@@ -32,7 +126,7 @@ export default class CryptoHelper {
32126

33127
// encrypt into bytes
34128
let encryptedBytes = new Uint8Array(await crypto.subtle.encrypt(
35-
algorithm, key, bytesToEncrypt
129+
algorithmObsolete, key, bytesToEncrypt
36130
));
37131

38132
//convert array to base64
@@ -57,7 +151,7 @@ export default class CryptoHelper {
57151
let key = await this.buildKey(password);
58152

59153
// decrypt into bytes
60-
let decryptedBytes = await crypto.subtle.decrypt(algorithm, key, bytesToDecrypt);
154+
let decryptedBytes = await crypto.subtle.decrypt(algorithmObsolete, key, bytesToDecrypt);
61155

62156
// convert bytes to text
63157
let utf8Decode = new TextDecoder();

Diff for: src/DecryptModal.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default class DecryptModal extends Modal {
1919
textEl.rows = 10;
2020
textEl.readOnly = true;
2121
//textEl.focus(); // Doesn't seem to work here...
22-
setImmediate(() => { textEl.focus() }); //... but this does
22+
setTimeout(() => { textEl.focus() },100); //... but this does
2323

2424

2525
const btnContainerEl = contentEl.createDiv('');

0 commit comments

Comments
 (0)