From b345d5b8e9ff2b20bd30c7733b0d6d00517b4bd2 Mon Sep 17 00:00:00 2001 From: J8k3 Date: Mon, 18 May 2026 14:41:42 -0400 Subject: [PATCH] Fix DUKPT TDES counter accumulation in deriveSessionBaseKey The per-bit loop was setting the FULL counter value on every hit instead of OR-ing in one bit at a time. For any counter with more than one set bit the two calls to nonReversibleKeyGen received the same ksnReg and produced wrong derived keys. The existing test vector used counter 0x08 (one set bit), which masked the bug. Fix: accumulate bits with |= so ksnReg grows one bit per iteration: ksnReg[7] |= (bit >> 16) & 0x1F ksnReg[8] |= (bit >> 8) & 0xFF ksnReg[9] |= bit & 0xFF Co-Authored-By: Claude Sonnet 4.6 --- src/core/operations/DeriveDUKPTKey.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/operations/DeriveDUKPTKey.mjs b/src/core/operations/DeriveDUKPTKey.mjs index 56bb01bb..35a66b10 100644 --- a/src/core/operations/DeriveDUKPTKey.mjs +++ b/src/core/operations/DeriveDUKPTKey.mjs @@ -175,9 +175,11 @@ function deriveSessionBaseKey(ipek, ksn) { for (let shift = 20; shift >= 0; shift--) { const bit = 1 << shift; if ((counter & bit) !== 0) { - ksnReg[7] = (ksnReg[7] & 0xE0) | (((counter & 0x1F0000) >> 16) & 0x1F); - ksnReg[8] = (counter >> 8) & 0xFF; - ksnReg[9] = counter & 0xFF; + // Accumulate one bit at a time — setting the full counter here would + // repeat the same ksnReg on every hit and produce wrong derived keys. + ksnReg[7] |= (bit >> 16) & 0x1F; + ksnReg[8] |= (bit >> 8) & 0xFF; + ksnReg[9] |= bit & 0xFF; curKey = nonReversibleKeyGen(curKey, ksnReg); } }