Skip to content
This repository has been archived by the owner on Jun 7, 2019. It is now read-only.

Add validation to decrypt passphrase - Closes #688 #709

Merged
merged 1 commit into from
Jul 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions packages/lisk-cryptography/src/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ export const bufferToBigNumberString = bigNumberBuffer =>
export const bufferToHex = buffer => Buffer.from(buffer).toString('hex');

const hexRegex = /^[0-9a-f]+/i;
export const hexToBuffer = hex => {
export const hexToBuffer = (hex, argumentName = 'Argument') => {
if (typeof hex !== 'string') {
throw new TypeError('Argument must be a string.');
throw new TypeError(`${argumentName} must be a string.`);
}
const matchedHex = (hex.match(hexRegex) || [])[0];
if (!matchedHex || matchedHex.length !== hex.length) {
throw new TypeError('Argument must be a valid hex string.');
throw new TypeError(`${argumentName} must be a valid hex string.`);
}
if (matchedHex.length % 2 !== 0) {
throw new TypeError('Argument must have a valid length of hex string.');
throw new TypeError(
`${argumentName} must have a valid length of hex string.`,
);
}
return Buffer.from(matchedHex, 'hex');
};
Expand Down Expand Up @@ -82,7 +84,7 @@ export const stringifyEncryptedPassphrase = encryptedPassphrase => {
iv: encryptedPassphrase.iv,
tag: encryptedPassphrase.tag,
version: encryptedPassphrase.version,
};
};
return querystring.stringify(objectToStringify);
};

Expand Down
29 changes: 21 additions & 8 deletions packages/lisk-cryptography/src/encrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,36 @@ const encryptAES256GCMWithPassword = (
};

const getTagBuffer = tag => {
const tagBuffer = hexToBuffer(tag);
const tagBuffer = hexToBuffer(tag, 'Tag');
if (tagBuffer.length !== 16) {
throw new Error('Tag must be 16 bytes.');
}
return tagBuffer;
};

const decryptAES256GCMWithPassword = (
{ iterations = PBKDF2_ITERATIONS, cipherText, iv, salt, tag },
password,
) => {
const decryptAES256GCMWithPassword = (encryptedPassphrase, password) => {
const {
iterations = PBKDF2_ITERATIONS,
cipherText,
iv,
salt,
tag,
} = encryptedPassphrase;

const tagBuffer = getTagBuffer(tag);
const key = getKeyFromPassword(password, hexToBuffer(salt), iterations);
const key = getKeyFromPassword(
password,
hexToBuffer(salt, 'Salt'),
iterations,
);

const decipher = crypto.createDecipheriv('aes-256-gcm', key, hexToBuffer(iv));
const decipher = crypto.createDecipheriv(
'aes-256-gcm',
key,
hexToBuffer(iv, 'IV'),
);
decipher.setAuthTag(tagBuffer);
const firstBlock = decipher.update(hexToBuffer(cipherText));
const firstBlock = decipher.update(hexToBuffer(cipherText, 'Cipher text'));
const decrypted = Buffer.concat([firstBlock, decipher.final()]);

return decrypted.toString();
Expand Down
20 changes: 19 additions & 1 deletion packages/lisk-cryptography/test/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ describe('convert', () => {
);
});

it('should throw an error for a non-string input with custom argument name', () => {
return expect(hexToBuffer.bind(null, {}, 'Custom')).to.throw(
'Custom must be a string.',
);
});

it('should throw TypeError with non hex string', () => {
return expect(hexToBuffer.bind(null, 'yKJj')).to.throw(
TypeError,
Expand Down Expand Up @@ -109,12 +115,24 @@ describe('convert', () => {
);
});

it('should throw TypeError with odd number of hex string', () => {
it('should throw an error for a non-hex string input with custom argument name', () => {
return expect(hexToBuffer.bind(null, 'yKJj', 'Custom')).to.throw(
'Custom must be a valid hex string.',
);
});

it('should throw TypeError with odd-length hex string', () => {
return expect(hexToBuffer.bind(null, 'c3a5c3a4c3b6a')).to.throw(
TypeError,
'Argument must have a valid length of hex string.',
);
});

it('should throw an error for an odd-length hex string input with custom argument name', () => {
return expect(hexToBuffer.bind(null, 'c3a5c3a4c3b6a', 'Custom')).to.throw(
'Custom must have a valid length of hex string.',
);
});
});

describe('#getFirstEightBytesReversed', () => {
Expand Down
46 changes: 45 additions & 1 deletion packages/lisk-cryptography/test/encrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,50 @@ describe('encrypt', () => {
return expect(decrypted).to.be.equal(defaultPassphrase);
});

it('should inform the user if cipherText is missing', () => {
delete encryptedPassphrase.cipherText;
return expect(
decryptPassphraseWithPassword.bind(
null,
encryptedPassphrase,
defaultPassword,
),
).to.throw('Cipher text must be a string.');
});

it('should inform the user if iv is missing', () => {
delete encryptedPassphrase.iv;
return expect(
decryptPassphraseWithPassword.bind(
null,
encryptedPassphrase,
defaultPassword,
),
).to.throw('IV must be a string.');
});

it('should inform the user if salt is missing', () => {
delete encryptedPassphrase.salt;
return expect(
decryptPassphraseWithPassword.bind(
null,
encryptedPassphrase,
defaultPassword,
),
).to.throw('Salt must be a string.');
});

it('should inform the user if tag is missing', () => {
delete encryptedPassphrase.tag;
return expect(
decryptPassphraseWithPassword.bind(
null,
encryptedPassphrase,
defaultPassword,
),
).to.throw('Tag must be a string.');
});

it('should inform the user if the salt has been altered', () => {
encryptedPassphrase.salt = `00${encryptedPassphrase.salt.slice(2)}`;
return expect(
Expand Down Expand Up @@ -294,7 +338,7 @@ describe('encrypt', () => {
encryptedPassphrase,
defaultPassword,
),
).to.throw('Argument must be a valid hex string.');
).to.throw('Tag must be a valid hex string.');
});

it('should inform the user if the tag has been altered', () => {
Expand Down