This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/max-length-dkt in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit dc2b6e08d2b6d10d815272bcf1ed5dec67bc3c41 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Sep 4 07:07:35 2026 +0100 Put a max bound on Derived Key offset + length --- .../wss4j/common/derivedKey/DerivedKeyUtils.java | 26 +++++++++ .../common/derivedKey/DerivedKeyUtilsTest.java | 63 ++++++++++++++++++++++ .../input/DerivedKeyTokenInputHandler.java | 22 +++++++- 3 files changed, 109 insertions(+), 2 deletions(-) diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java b/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java index 08fe625ee..7d72270c4 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtils.java @@ -33,6 +33,18 @@ public final class DerivedKeyUtils { */ public static final int MINIMUM_DERIVED_KEY_LENGTH = 16; + /** + * The maximum length in bytes of a derived key. wsc:Length and wsc:Offset are + * attacker-controlled message content, and the P_SHA-1 derivation allocates + * offset+length bytes and performs one HMAC operation per 20 output bytes. + */ + public static final int MAXIMUM_DERIVED_KEY_LENGTH = 512; + + /** + * The maximum value in bytes of the wsc:Offset of a derived key. + */ + public static final int MAXIMUM_DERIVED_KEY_OFFSET = 4096; + private DerivedKeyUtils() { // complete } @@ -71,6 +83,20 @@ public final class DerivedKeyUtils { + " byte(s) is less than the minimum allowed (" + MINIMUM_DERIVED_KEY_LENGTH + " bytes)"}); } + if (keyLength > MAXIMUM_DERIVED_KEY_LENGTH) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, + "unsupportedKeyId", + new Object[] {"Requested derived key length of " + keyLength + + " bytes exceeds the maximum allowed (" + + MAXIMUM_DERIVED_KEY_LENGTH + " bytes)"}); + } + if (offset < 0 || offset > MAXIMUM_DERIVED_KEY_OFFSET) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, + "unsupportedKeyId", + new Object[] {"Requested derived key offset of " + offset + + " bytes is outside the allowed range [0, " + + MAXIMUM_DERIVED_KEY_OFFSET + "]"}); + } return algo.createKey(secret, seed, offset, keyLength); } } diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java b/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java index bf50941bd..ba353d688 100644 --- a/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/derivedKey/DerivedKeyUtilsTest.java @@ -55,6 +55,69 @@ class DerivedKeyUtilsTest { Assertions.assertEquals(DerivedKeyUtils.MINIMUM_DERIVED_KEY_LENGTH, key.length); } + @Test + void rejectsDerivedKeyLengthAboveMaximum() { + WSSecurityException exception = Assertions.assertThrows(WSSecurityException.class, + () -> DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + DerivedKeyUtils.MAXIMUM_DERIVED_KEY_LENGTH + 1, + SECRET, + NONCE, + 0)); + + Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, exception.getErrorCode()); + } + + @Test + void rejectsDerivedKeyOffsetOutsideMaximum() { + WSSecurityException exception = Assertions.assertThrows(WSSecurityException.class, + () -> DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + DerivedKeyUtils.MINIMUM_DERIVED_KEY_LENGTH, + SECRET, + NONCE, + DerivedKeyUtils.MAXIMUM_DERIVED_KEY_OFFSET + 1)); + + Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, exception.getErrorCode()); + } + + @Test + void rejectsNegativeDerivedKeyOffset() { + WSSecurityException exception = Assertions.assertThrows(WSSecurityException.class, + () -> DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + DerivedKeyUtils.MINIMUM_DERIVED_KEY_LENGTH, + SECRET, + NONCE, + -1)); + + Assertions.assertEquals(WSSecurityException.ErrorCode.INVALID_SECURITY, exception.getErrorCode()); + } + + @Test + void acceptsNonDefaultDerivedKeyLengthAndOffset() throws Exception { + byte[] key = DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + 24, + SECRET, + NONCE, + 32); + byte[] keyWithDefaultOffset = DerivedKeyUtils.deriveKey( + ConversationConstants.DerivationAlgorithm.P_SHA_1, + null, + 24, + SECRET, + NONCE, + 0); + + Assertions.assertEquals(24, key.length); + Assertions.assertFalse(java.util.Arrays.equals(keyWithDefaultOffset, key)); + } + @Test void preservesDefaultDerivedKeyLength() throws Exception { byte[] key = DerivedKeyUtils.deriveKey( diff --git a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/DerivedKeyTokenInputHandler.java b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/DerivedKeyTokenInputHandler.java index 71c4822e4..ccbce361e 100644 --- a/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/DerivedKeyTokenInputHandler.java +++ b/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/DerivedKeyTokenInputHandler.java @@ -18,6 +18,7 @@ */ package org.apache.wss4j.stax.impl.processor.input; +import java.math.BigInteger; import java.security.Key; import java.util.Deque; import java.util.List; @@ -133,10 +134,12 @@ public class DerivedKeyTokenInputHandler extends AbstractInputSecurityHeaderHand byte[] keyBytes = DerivedKeyUtils.deriveKey( derivedKeyAlgorithm, derivedKeyTokenType.getLabel(), - derivedKeyTokenType.getLength().intValue(), + getDerivedKeyParameter(derivedKeyTokenType.getLength(), + DerivedKeyUtils.MAXIMUM_DERIVED_KEY_LENGTH, "length"), secret, nonce, - derivedKeyTokenType.getOffset().intValue() + getDerivedKeyParameter(derivedKeyTokenType.getOffset(), + DerivedKeyUtils.MAXIMUM_DERIVED_KEY_OFFSET, "offset") ); XMLSecurityConstants.AlgorithmUsage derivedKeyAlgorithmUsage; if (WSSConstants.Enc.equals(algorithmUsage)) { @@ -183,4 +186,19 @@ public class DerivedKeyTokenInputHandler extends AbstractInputSecurityHeaderHand derivedKeyTokenSecurityEvent.setCorrelationID(derivedKeyTokenType.getId()); inputProcessorChain.getSecurityContext().registerSecurityEvent(derivedKeyTokenSecurityEvent); } + + static int getDerivedKeyParameter(BigInteger value, int maximum, String name) throws WSSecurityException { + if (value == null) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, + "unsupportedKeyId", + new Object[] {"Missing derived key " + name}); + } + if (value.signum() < 0 || value.compareTo(BigInteger.valueOf(maximum)) > 0) { + throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, + "unsupportedKeyId", + new Object[] {"Requested derived key " + name + " of " + value + + " bytes is outside the allowed range [0, " + maximum + "]"}); + } + return value.intValue(); + } }
