From 26d9b3108f820dfd3a02fc33f7827ae74a5109a5 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 21 Sep 2022 15:30:35 +0200 Subject: [PATCH] Reject costly hashed proofs --- src/verifications.js | 30 ++++++++++++++++++++++++++++-- test/verifications.test.js | 13 ++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/verifications.js b/src/verifications.js index 7e1b7b3..c5ce662 100644 --- a/src/verifications.js +++ b/src/verifications.js @@ -38,15 +38,34 @@ const containsProof = async (data, fingerprint, claimFormat) => { let match while (!result && (match = hashRe.exec(data)) != null) { + let timeoutHandle + const timeoutPromise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout( + () => { + resolve(false) + }, 1000 + ) + }) + switch (match[1]) { case '2a': case '2b': case '2y': try { - result = await bcryptVerify({ + // Patch until promise.race properly works on WASM + if (parseInt(match[0].split('$')[2]) > 12) continue + + const hashPromise = bcryptVerify({ password: fingerprintURI, hash: match[0] }) + .then(result => result) + .catch(_ => false) + + result = await Promise.race([hashPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result + }) } catch (err) { result = false } @@ -57,10 +76,17 @@ const containsProof = async (data, fingerprint, claimFormat) => { case 'argon2d': case 'argon2id': try { - result = await argon2Verify({ + const hashPromise = argon2Verify({ password: fingerprintURI, hash: match[0] }) + .then(result => result) + .catch(_ => false) + + result = await Promise.race([hashPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result + }) } catch (err) { result = false } diff --git a/test/verifications.test.js b/test/verifications.test.js index 99ed401..76ff6d1 100644 --- a/test/verifications.test.js +++ b/test/verifications.test.js @@ -38,6 +38,9 @@ const bcryptCorrectProofData = [ const bcryptIncorrectProofData = [ '$2y$10$iHUhy320iUqJRVh7a/WlneAuJA/xRI/YEv7qxW8jfCDVmC7bmezX2' ] +const bcryptCostlyProofData = [ + '$2y$16$4Knuu11ZyPXa1qxEbEsKQemKY6ZHM8Bk7WElYfL8q5kmzNjY1Ty8W' +] const claimData = doipjs.claimDefinitions.data.irc.processURI('irc://domain.tld/test') describe('verifications.run', () => { @@ -45,7 +48,7 @@ describe('verifications.run', () => { const result = await doipjs.verifications.run(plaintextCorrectProofData, claimData, fingerprint) expect(result.result).to.be.true }) - it('should not verify a wrong plaintext proof', async () => { + it('should reject a wrong plaintext proof', async () => { const result = await doipjs.verifications.run(plaintextIncorrectProofData, claimData, fingerprint) expect(result.result).to.be.false }) @@ -53,7 +56,7 @@ describe('verifications.run', () => { const result = await doipjs.verifications.run(argon2CorrectProofData, claimData, fingerprint) expect(result.result).to.be.true }) - it('should not verify a wrong argon2-hashed proof', async () => { + it('should reject a wrong argon2-hashed proof', async () => { const result = await doipjs.verifications.run(argon2IncorrectProofData, claimData, fingerprint) expect(result.result).to.be.false }) @@ -61,8 +64,12 @@ describe('verifications.run', () => { const result = await doipjs.verifications.run(bcryptCorrectProofData, claimData, fingerprint) expect(result.result).to.be.true }) - it('should not verify a wrong bcrypt-hashed proof', async () => { + it('should reject a wrong bcrypt-hashed proof', async () => { const result = await doipjs.verifications.run(bcryptIncorrectProofData, claimData, fingerprint) expect(result.result).to.be.false }) + it('should reject a too costly hashed proof', async () => { + const result = await doipjs.verifications.run(bcryptCostlyProofData, claimData, fingerprint) + expect(result.result).to.be.false + }) })