Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions java/ql/lib/change-notes/2025-09-02-kdf-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added taint flow model for `java.crypto.KDF`.
15 changes: 15 additions & 0 deletions java/ql/lib/ext/javax.crypto.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ extensions:
- ["javax.crypto", "Cipher", True, "init", "(int,Key,AlgorithmParameterSpec,SecureRandom)", "", "Argument[2]", "encryption-iv", "manual"]
- ["javax.crypto", "Cipher", False, "unwrap", "(byte[],String,int)", "", "Argument[0]", "credentials-key", "hq-generated"]
- ["javax.crypto", "CipherSpi", True, "engineUnwrap", "(byte[],String,int)", "", "Argument[0]", "credentials-key", "hq-generated"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["javax.crypto", "KDF", False, "getInstance", "(String)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
- ["javax.crypto", "KDF", False, "getInstance", "(String,Provider)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
- ["javax.crypto", "KDF", False, "getInstance", "(String,String)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
- ["javax.crypto", "KDF", False, "getInstance", "(String,KDFParameters)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
- ["javax.crypto", "KDF", False, "getInstance", "(String,KDFParameters,Provider)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
- ["javax.crypto", "KDF", False, "getInstance", "(String,KDFParameters,String)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
- ["javax.crypto", "KDF", True, "getAlgorithm", "()", "", "Argument[this].SyntheticField[javax.crypto.KDF.algorithm]", "ReturnValue", "value", "manual"]
- ["javax.crypto", "KDF", True, "getProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["javax.crypto", "KDF", True, "deriveKey", "(String,AlgorithmParameterSpec)", "", "Argument[1]", "ReturnValue", "taint", "manual"]
- ["javax.crypto", "KDF", True, "deriveData", "(AlgorithmParameterSpec)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["javax.crypto", "SecretKey", True, "getEncoded", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- addsTo:
pack: codeql/java-all
extensible: neutralModel
Expand Down
15 changes: 15 additions & 0 deletions java/ql/lib/ext/javax.crypto.spec.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ extensions:
- ["javax.crypto.spec", "GCMParameterSpec", True, "GCMParameterSpec", "", "", "Argument[1]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "RC2ParameterSpec", True, "RC2ParameterSpec", "", "", "Argument[1]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "RC5ParameterSpec", True, "RC5ParameterSpec", "", "", "Argument[3]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(SecretKey)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(SecretKey)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(SecretKey)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(SecretKey)", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "thenExpand", "(byte[],int)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "thenExpand", "(byte[],int)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "thenExpand", "(byte[],int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec", False, "expandOnly", "(SecretKey,byte[],int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["javax.crypto.spec", "HKDFParameterSpec", False, "expandOnly", "(SecretKey,byte[],int)", "", "Argument[1]", "ReturnValue", "taint", "manual"]
- ["javax.crypto.spec", "SecretKeySpec", False, "SecretKeySpec", "(byte[],String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["javax.crypto.spec", "SecretKeySpec", False, "SecretKeySpec", "(byte[],int,int,String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- addsTo:
pack: codeql/java-all
extensible: sinkModel
Expand Down
87 changes: 87 additions & 0 deletions java/ql/test/library-tests/dataflow/kdf/KDFDataflowTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import javax.crypto.KDF;
import javax.crypto.spec.HKDFParameterSpec;

public class KDFDataflowTest {
public static String source(String label) {
return "tainted";
}

public static void sink(Object o) {}

public static void main(String[] args) throws Exception {
String userInput = source("");
byte[] taintedBytes = userInput.getBytes();

testBuilderPattern(taintedBytes);
testSeparateBuilder(taintedBytes);
testKDFWithSalt(taintedBytes);
testStaticParameterSpec(taintedBytes);
testCleanUsage();
}

public static void testBuilderPattern(byte[] taintedIKM) throws Exception {
HKDFParameterSpec.Builder builder = HKDFParameterSpec.ofExtract();
builder.addIKM(taintedIKM);
HKDFParameterSpec spec = builder.thenExpand("info".getBytes(), 32);

KDF kdf = KDF.getInstance("HKDF-SHA256");
byte[] result = kdf.deriveData(spec);
sink(result); // $ hasTaintFlow
}

public static void testSeparateBuilder(byte[] taintedIKM) throws Exception {
HKDFParameterSpec.Builder builder1 = HKDFParameterSpec.ofExtract();
HKDFParameterSpec.Builder builder2 = builder1.addIKM(taintedIKM);
HKDFParameterSpec spec = builder2.thenExpand("info".getBytes(), 32);

KDF kdf = KDF.getInstance("HKDF-SHA256");
byte[] result = kdf.deriveData(spec);
sink(result); // $ hasTaintFlow
}

public static void testKDFWithSalt(byte[] taintedIKM) throws Exception {
HKDFParameterSpec.Builder builder = HKDFParameterSpec.ofExtract();
builder.addIKM(taintedIKM);
builder.addSalt("sensitive-salt".getBytes());
HKDFParameterSpec spec = builder.thenExpand("info".getBytes(), 32);

KDF kdf = KDF.getInstance("HKDF-SHA256");
byte[] result = kdf.deriveData(spec);
sink(result); // $ hasTaintFlow
}

public static void testStaticParameterSpec(byte[] taintedIKM) throws Exception {
javax.crypto.spec.SecretKeySpec secretKey = new javax.crypto.spec.SecretKeySpec(taintedIKM, "AES");
HKDFParameterSpec spec = HKDFParameterSpec.expandOnly(
secretKey, "info".getBytes(), 32);

KDF kdf = KDF.getInstance("HKDF-SHA256");
byte[] result = kdf.deriveData(spec);
sink(result); // $ hasTaintFlow
}

public static void testCleanUsage() throws Exception {
byte[] cleanKeyMaterial = "static-key-material".getBytes();

HKDFParameterSpec.Builder builder = HKDFParameterSpec.ofExtract();
builder.addIKM(cleanKeyMaterial);
HKDFParameterSpec spec = builder.thenExpand("info".getBytes(), 32);

KDF kdf = KDF.getInstance("HKDF-SHA256");
byte[] cleanResult = kdf.deriveData(spec);
sink(cleanResult); // Safe - no taint
}

public static void testThenExpand(byte[] cleanIKM) throws Exception {
String userInput = source("");
byte[] taintedInfo = userInput.getBytes();

HKDFParameterSpec.Builder builder = HKDFParameterSpec.ofExtract();
builder.addIKM(cleanIKM);
HKDFParameterSpec spec = builder.thenExpand(taintedInfo, 32);

KDF kdf = KDF.getInstance("HKDF-SHA256");
byte[] result = kdf.deriveData(spec);
sink(result); // $ hasTaintFlow
}
}
1 change: 1 addition & 0 deletions java/ql/test/library-tests/dataflow/kdf/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args --enable-preview --release 25
Loading