diff --git a/lib/util/crypto.js b/lib/util/crypto.js index 6242ce289..9a21232b6 100644 --- a/lib/util/crypto.js +++ b/lib/util/crypto.js @@ -36,7 +36,7 @@ const BCRYPT_COST_FACTOR = process.env.BCRYPT === 'insecure' ? 1 : 12; const hashPassword = (plain) => { if (typeof plain !== 'string') return reject(Problem.user.invalidDataTypeOfParameter({ value: plain, expected: 'string' })); if (plain.length < 10) return reject(Problem.user.passwordTooShort()); - if (plain.length > 72) return reject(Problem.user.passwordTooLong()); + if (Buffer.byteLength(plain) > 72) return reject(Problem.user.passwordTooLong()); return isBlank(plain) ? resolve(null) : bcrypt.hash(plain, BCRYPT_COST_FACTOR); }; const verifyPassword = (plain, hash) => ((typeof plain !== 'string' || isBlank(plain) || isBlank(hash)) diff --git a/test/unit/util/crypto.js b/test/unit/util/crypto.js index a9e0a61e5..be9580684 100644 --- a/test/unit/util/crypto.js +++ b/test/unit/util/crypto.js @@ -21,6 +21,25 @@ describe('util/crypto', () => { it('should reject given a blank plaintext', () => hashPassword('').should.be.rejectedWith('The password or passphrase provided does not meet the required length.')); + + it('should reject given a short plaintext', () => + hashPassword('2short').should.be.rejectedWith('The password or passphrase provided does not meet the required length.')); + + it('should reject given a short plaintext (measured in bytes)', () => + // This emoji is a single char on some devices, but in UTF-8 is 11 bytes. A + // single character is too short to use as a password, even if its byte length + // is above the password length limit. + hashPassword('👩‍💻').should.be.rejectedWith('The password or passphrase provided does not meet the required length.')); + + it('should reject given a long plaintext', () => + hashPassword('longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg').should.be.rejectedWith('The password or passphrase provided exceeds the maximum length.')); + + it('should reject given a long plaintext (measured in bytes)', () => { + const password = '❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️❤️'; + password.length.should.be.lessThan(72); + Buffer.byteLength(password).should.be.greaterThan(72); + return hashPassword(password).should.be.rejectedWith('The password or passphrase provided exceeds the maximum length.'); + }); }); describe('verifyPassword()', () => {