Skip to content

Commit 967abe0

Browse files
committed
Replace createDecipher with createDecipheriv and add a compatibility mode for newer nodejs versions.
1 parent ac1b554 commit 967abe0

File tree

5 files changed

+70
-39
lines changed

5 files changed

+70
-39
lines changed

examples/getClient.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ module.exports = ({
1010
userId,
1111
hostId,
1212
passphrase,
13+
iv,
1314
keyStoragePath,
1415
} = loadConfig()) => new Client({
1516
url,
1617
partnerId,
1718
userId,
1819
hostId,
1920
passphrase,
21+
iv,
2022
keyStorage: fsKeysStorage(keyStoragePath),
2123
});

lib/Client.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ const stringifyKeys = (keys) => {
4343
* @property {string} partnerId - PARTNERID provided by the bank
4444
* @property {string} hostId - HOSTID provided by the bank
4545
* @property {string} userId - USERID provided by the bank
46-
* @property {string} passphrase - passphrase for keys encryption
46+
* @property {string|Buffer} passphrase - passphrase or key for keys encryption
47+
* @property {string|Buffer} iv - Initialization Vector for keys encryption
4748
* @property {KeyStorage} keyStorage - keyStorage implementation
4849
* @property {object} [tracesStorage] - traces (logs) storage implementation
4950
* @property {string} bankName - Full name of the bank to be used in the bank INI letters.
5051
* @property {string} bankShortName - Short name of the bank to be used in folders, filenames etc.
5152
* @property {string} languageCode - Language code to be used in the bank INI letters ("de", "en" and "fr" are currently supported).
5253
* @property {string} storageLocation - Location where to store the files that are downloaded. This can be a network share for example.
5354
*/
54-
5555
module.exports = class Client {
5656
/**
5757
*Creates an instance of Client.
@@ -63,6 +63,7 @@ module.exports = class Client {
6363
userId,
6464
hostId,
6565
passphrase,
66+
iv,
6667
keyStorage,
6768
tracesStorage,
6869
bankName,
@@ -88,7 +89,7 @@ module.exports = class Client {
8889
this.userId = userId;
8990
this.hostId = hostId;
9091
this.keyStorage = keyStorage;
91-
this.keyEncryptor = defaultKeyEncryptor({ passphrase });
92+
this.keyEncryptor = defaultKeyEncryptor({ passphrase, iv });
9293
this.tracesStorage = tracesStorage || null;
9394
this.bankName = bankName || 'Dummy Bank Full Name';
9495
this.bankShortName = bankShortName || 'BANKSHORTCODE';
@@ -249,7 +250,6 @@ module.exports = class Client {
249250
async keys() {
250251
try {
251252
const keysString = await this._readKeys();
252-
253253
return new Keys(JSON.parse(this.keyEncryptor.decrypt(keysString)));
254254
} catch (err) {
255255
return null;

lib/crypto/encryptDecrypt.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
const crypto = require('crypto');
4+
5+
const createKeyAndIv = (passphrase) => {
6+
// this generates a 256-bit key and a 128-bit iv for aes-256-cbc
7+
// just like nodejs's deprecated/removed crypto.createCipher would
8+
const a = crypto.createHash('md5').update(passphrase).digest();
9+
const b = crypto
10+
.createHash('md5')
11+
.update(Buffer.concat([a, Buffer.from(passphrase)]))
12+
.digest();
13+
const c = crypto
14+
.createHash('md5')
15+
.update(Buffer.concat([b, Buffer.from(passphrase)]))
16+
.digest();
17+
const bytes = Buffer.concat([a, b, c]);
18+
const key = bytes.subarray(0, 32);
19+
const iv = bytes.subarray(32, 48);
20+
return { key, iv };
21+
};
22+
23+
const encrypt = (data, algorithm, passphrase, iv) => {
24+
let cipher;
25+
if (iv) {
26+
cipher = crypto.createCipheriv(algorithm, passphrase, iv);
27+
} else {
28+
console.warn(
29+
'[Deprecation notice] No IV provided, falling back to legacy key derivation.',
30+
);
31+
console.warn(
32+
'This will be removed in a future major release. You should encrypt your keys with a proper key and IV.',
33+
);
34+
const { key, iv: generatedIv } = createKeyAndIv(passphrase);
35+
cipher = crypto.createCipheriv(algorithm, key, generatedIv);
36+
}
37+
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
38+
return Buffer.from(encrypted).toString('base64');
39+
};
40+
41+
const decrypt = (data, algorithm, passphrase, iv) => {
42+
data = Buffer.from(data, 'base64').toString();
43+
let decipher;
44+
if (iv) {
45+
decipher = crypto.createDecipheriv(algorithm, passphrase, iv);
46+
} else {
47+
console.warn(
48+
'[Deprecation notice] No IV provided, falling back to legacy key derivation.',
49+
);
50+
console.warn(
51+
'This will be removed in a future major release. You should re-encrypt your keys with a proper key and IV.',
52+
);
53+
const { key, iv: generatedIv } = createKeyAndIv(passphrase);
54+
decipher = crypto.createDecipheriv(algorithm, key, generatedIv);
55+
}
56+
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
57+
return decrypted;
58+
};
59+
60+
module.exports = { encrypt, decrypt };

lib/keymanagers/KeysManager.js

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,8 @@
11
'use strict';
22

3-
const crypto = require('crypto');
4-
3+
const { encrypt, decrypt } = require('../crypto/encryptDecrypt');
54
const Keys = require('./Keys');
65

7-
const encrypt = (data, algorithm, passphrase) => {
8-
const cipher = crypto.createCipher(algorithm, passphrase);
9-
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
10-
11-
return Buffer.from(encrypted).toString('base64');
12-
};
13-
const decrypt = (data, algorithm, passphrase) => {
14-
data = (Buffer.from(data, 'base64')).toString();
15-
16-
const decipher = crypto.createDecipher(algorithm, passphrase);
17-
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
18-
19-
return decrypted;
20-
};
21-
226
module.exports = (keysStorage, passphrase, algorithm = 'aes-256-cbc') => {
237
const storage = keysStorage;
248
const pass = passphrase;
Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,9 @@
11
'use strict';
22

3-
const crypto = require('crypto');
3+
const { encrypt, decrypt } = require('../crypto/encryptDecrypt');
44

5-
const encrypt = (data, algorithm, passphrase) => {
6-
const cipher = crypto.createCipher(algorithm, passphrase);
7-
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
8-
return Buffer.from(encrypted).toString('base64');
9-
};
10-
const decrypt = (data, algorithm, passphrase) => {
11-
data = (Buffer.from(data, 'base64')).toString();
12-
const decipher = crypto.createDecipher(algorithm, passphrase);
13-
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
145

15-
return decrypted;
16-
};
17-
18-
module.exports = ({
19-
passphrase,
20-
algorithm = 'aes-256-cbc',
21-
}) => ({
22-
encrypt: data => encrypt(data, algorithm, passphrase),
6+
module.exports = ({ passphrase, iv, algorithm = 'aes-256-cbc' }) => ({
7+
encrypt: data => encrypt(data, algorithm, passphrase, iv),
238
decrypt: data => decrypt(data, algorithm, passphrase),
249
});

0 commit comments

Comments
 (0)