From: Justin Wind <justin.wind+git@gmail.com>
Date: Tue, 21 Jan 2025 20:55:37 +0000 (-0800)
Subject: use constant-time check when validating codes
X-Git-Tag: v1.1.6~2
X-Git-Url: https://git.squeep.com/?a=commitdiff_plain;h=04e9bd88c71376fbc42326edff566bd9d55fda5d;p=squeep-totp

use constant-time check when validating codes
---

diff --git a/lib/hotp.js b/lib/hotp.js
index 1fa41c9..052fab1 100644
--- a/lib/hotp.js
+++ b/lib/hotp.js
@@ -128,7 +128,10 @@ class HMACBasedOneTimePassword {
    */
   validate(hotp, count) {
     const codeString = this.generate(count);
-    return codeString === hotp.trim();
+    const codeStringB = Buffer.from(codeString);
+    const hotpB = Buffer.from(hotp.trim());
+    return codeStringB.length === hotpB.length
+      && crypto.timingSafeEqual(hotpB, codeStringB);
   }
 
   /**
diff --git a/lib/totp.js b/lib/totp.js
index aff5d96..2e21009 100644
--- a/lib/totp.js
+++ b/lib/totp.js
@@ -1,6 +1,7 @@
 'use strict';
 
 const HOTP = require('./hotp');
+const crypto = require('node:crypto');
 
 class TimeBasedOneTimePassword extends HOTP {
   /**
@@ -80,9 +81,11 @@ class TimeBasedOneTimePassword extends HOTP {
    */
   validate(hotp, count) {
     const counter = count ?? this.counter;
+    const hotpB = Buffer.from(hotp.trim());
     for (const offset of this.driftOffsets) {
       const codeString = this.generate(counter + offset);
-      if (codeString === hotp.trim()) {
+      const codeStringB = Buffer.from(codeString);
+      if (hotpB.length === codeString.length && crypto.timingSafeEqual(hotpB, codeStringB)) {
         return true;
       }
     }