diff --git a/java/ql/lib/change-notes/2025-09-02-kdf-api.md b/java/ql/lib/change-notes/2025-09-02-kdf-api.md new file mode 100644 index 000000000000..db812e907780 --- /dev/null +++ b/java/ql/lib/change-notes/2025-09-02-kdf-api.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added taint flow model for `java.crypto.KDF`. \ No newline at end of file diff --git a/java/ql/lib/ext/javax.crypto.model.yml b/java/ql/lib/ext/javax.crypto.model.yml index 2b3bfc1abe85..0a2d43fce17f 100644 --- a/java/ql/lib/ext/javax.crypto.model.yml +++ b/java/ql/lib/ext/javax.crypto.model.yml @@ -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 diff --git a/java/ql/lib/ext/javax.crypto.spec.model.yml b/java/ql/lib/ext/javax.crypto.spec.model.yml index d2b7dbc99b8d..e670af7622ad 100644 --- a/java/ql/lib/ext/javax.crypto.spec.model.yml +++ b/java/ql/lib/ext/javax.crypto.spec.model.yml @@ -7,6 +7,20 @@ 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", 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 diff --git a/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected b/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected index b901340b9642..a56980d10ac4 100644 --- a/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected +++ b/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected @@ -16,30 +16,6 @@ methodWithDuplicate | AbstractCollection | removeAll | Collection | | AbstractCollection | retainAll | Collection | | AbstractCollection | toArray | T[] | -| AbstractCollection> | add | Entry | -| AbstractCollection> | addAll | Collection> | -| AbstractCollection> | contains | Object | -| AbstractCollection> | containsAll | Collection | -| AbstractCollection> | remove | Object | -| AbstractCollection> | removeAll | Collection | -| AbstractCollection> | retainAll | Collection | -| AbstractCollection> | toArray | T[] | -| AbstractCollection | add | K | -| AbstractCollection | addAll | Collection | -| AbstractCollection | contains | Object | -| AbstractCollection | containsAll | Collection | -| AbstractCollection | remove | Object | -| AbstractCollection | removeAll | Collection | -| AbstractCollection | retainAll | Collection | -| AbstractCollection | toArray | T[] | -| AbstractCollection | add | Runnable | -| AbstractCollection | addAll | Collection | -| AbstractCollection | contains | Object | -| AbstractCollection | containsAll | Collection | -| AbstractCollection | remove | Object | -| AbstractCollection | removeAll | Collection | -| AbstractCollection | retainAll | Collection | -| AbstractCollection | toArray | T[] | | AbstractCollection | add | String | | AbstractCollection | addAll | Collection | | AbstractCollection | contains | Object | @@ -56,14 +32,6 @@ methodWithDuplicate | AbstractCollection | removeAll | Collection | | AbstractCollection | retainAll | Collection | | AbstractCollection | toArray | T[] | -| AbstractCollection | add | V | -| AbstractCollection | addAll | Collection | -| AbstractCollection | contains | Object | -| AbstractCollection | containsAll | Collection | -| AbstractCollection | remove | Object | -| AbstractCollection | removeAll | Collection | -| AbstractCollection | retainAll | Collection | -| AbstractCollection | toArray | T[] | | AbstractList | add | E | | AbstractList | add | int | | AbstractList | addAll | Collection | @@ -103,14 +71,14 @@ methodWithDuplicate | AbstractMap | put | V | | AbstractMap | putAll | Map | | AbstractMap | remove | Object | -| AbstractMap> | containsKey | Object | -| AbstractMap> | containsValue | Object | -| AbstractMap> | equals | Object | -| AbstractMap> | get | Object | -| AbstractMap> | put | Entry | -| AbstractMap> | put | Identity | -| AbstractMap> | putAll | Map> | -| AbstractMap> | remove | Object | +| AbstractMap | containsKey | Object | +| AbstractMap | containsValue | Object | +| AbstractMap | equals | Object | +| AbstractMap | get | Object | +| AbstractMap | put | Identity | +| AbstractMap | put | Object | +| AbstractMap | putAll | Map | +| AbstractMap | remove | Object | | AbstractMap | containsKey | Object | | AbstractMap | containsValue | Object | | AbstractMap | equals | Object | @@ -179,17 +147,6 @@ methodWithDuplicate | Collection | retainAll | Collection | | Collection | toArray | IntFunction | | Collection | toArray | T[] | -| Collection | add | Runnable | -| Collection | addAll | Collection | -| Collection | contains | Object | -| Collection | containsAll | Collection | -| Collection | equals | Object | -| Collection | remove | Object | -| Collection | removeAll | Collection | -| Collection | removeIf | Predicate | -| Collection | retainAll | Collection | -| Collection | toArray | IntFunction | -| Collection | toArray | T[] | | Collection | add | String | | Collection | addAll | Collection | | Collection | contains | Object | @@ -332,37 +289,36 @@ methodWithDuplicate | Map | replace | K | | Map | replace | V | | Map | replaceAll | BiFunction | -| Map> | compute | BiFunction,? extends Entry> | -| Map> | compute | Identity | -| Map> | computeIfAbsent | Function> | -| Map> | computeIfAbsent | Identity | -| Map> | computeIfPresent | BiFunction,? extends Entry> | -| Map> | computeIfPresent | Identity | -| Map> | containsKey | Object | -| Map> | containsValue | Object | -| Map> | copyOf | Map | -| Map> | entry | K | -| Map> | entry | V | -| Map> | equals | Object | -| Map> | forEach | BiConsumer> | -| Map> | get | Object | -| Map> | getOrDefault | Entry | -| Map> | getOrDefault | Object | -| Map> | merge | BiFunction,? super Entry,? extends Entry> | -| Map> | merge | Entry | -| Map> | merge | Identity | -| Map> | of | K | -| Map> | of | V | -| Map> | ofEntries | Entry[] | -| Map> | put | Entry | -| Map> | put | Identity | -| Map> | putAll | Map> | -| Map> | putIfAbsent | Entry | -| Map> | putIfAbsent | Identity | -| Map> | remove | Object | -| Map> | replace | Entry | -| Map> | replace | Identity | -| Map> | replaceAll | BiFunction,? extends Entry> | +| Map | compute | BiFunction | +| Map | compute | Identity | +| Map | computeIfAbsent | Function | +| Map | computeIfAbsent | Identity | +| Map | computeIfPresent | BiFunction | +| Map | computeIfPresent | Identity | +| Map | containsKey | Object | +| Map | containsValue | Object | +| Map | copyOf | Map | +| Map | entry | K | +| Map | entry | V | +| Map | equals | Object | +| Map | forEach | BiConsumer | +| Map | get | Object | +| Map | getOrDefault | Object | +| Map | merge | BiFunction | +| Map | merge | Identity | +| Map | merge | Object | +| Map | of | K | +| Map | of | V | +| Map | ofEntries | Entry[] | +| Map | put | Identity | +| Map | put | Object | +| Map | putAll | Map | +| Map | putIfAbsent | Identity | +| Map | putIfAbsent | Object | +| Map | remove | Object | +| Map | replace | Identity | +| Map | replace | Object | +| Map | replaceAll | BiFunction | | Map | compute | BiFunction | | Map | compute | K | | Map | computeIfAbsent | Function | diff --git a/java/ql/test-kotlin1/library-tests/reflection/reflection.expected b/java/ql/test-kotlin1/library-tests/reflection/reflection.expected index 7ce456cdcbb2..3bc2f9ce67d6 100644 --- a/java/ql/test-kotlin1/library-tests/reflection/reflection.expected +++ b/java/ql/test-kotlin1/library-tests/reflection/reflection.expected @@ -289,6 +289,7 @@ compGenerated | file:///LongProgression.class:0:0:0:0 | spliterator | Forwarder for a Kotlin class inheriting an interface default method | | file:///LongRange.class:0:0:0:0 | forEach | Forwarder for a Kotlin class inheriting an interface default method | | file:///LongRange.class:0:0:0:0 | spliterator | Forwarder for a Kotlin class inheriting an interface default method | +| file:///String.class:0:0:0:0 | getChars | Forwarder for a Kotlin class inheriting an interface default method | | file:///String.class:0:0:0:0 | isEmpty | Forwarder for a Kotlin class inheriting an interface default method | | reflection.kt:7:49:7:54 | new Function2(...) { ... } | The class around a local function, a lambda, or a function reference | | reflection.kt:10:38:10:42 | new KProperty1(...) { ... } | The class around a local function, a lambda, or a function reference | diff --git a/java/ql/test-kotlin2/library-tests/java-kotlin-collection-type-generic-methods/test.expected b/java/ql/test-kotlin2/library-tests/java-kotlin-collection-type-generic-methods/test.expected index 03e86e2d2a18..2495c1fc1572 100644 --- a/java/ql/test-kotlin2/library-tests/java-kotlin-collection-type-generic-methods/test.expected +++ b/java/ql/test-kotlin2/library-tests/java-kotlin-collection-type-generic-methods/test.expected @@ -16,30 +16,6 @@ methodWithDuplicate | AbstractCollection | removeAll | Collection | | AbstractCollection | retainAll | Collection | | AbstractCollection | toArray | T[] | -| AbstractCollection> | add | Entry | -| AbstractCollection> | addAll | Collection> | -| AbstractCollection> | contains | Object | -| AbstractCollection> | containsAll | Collection | -| AbstractCollection> | remove | Object | -| AbstractCollection> | removeAll | Collection | -| AbstractCollection> | retainAll | Collection | -| AbstractCollection> | toArray | T[] | -| AbstractCollection | add | K | -| AbstractCollection | addAll | Collection | -| AbstractCollection | contains | Object | -| AbstractCollection | containsAll | Collection | -| AbstractCollection | remove | Object | -| AbstractCollection | removeAll | Collection | -| AbstractCollection | retainAll | Collection | -| AbstractCollection | toArray | T[] | -| AbstractCollection | add | Runnable | -| AbstractCollection | addAll | Collection | -| AbstractCollection | contains | Object | -| AbstractCollection | containsAll | Collection | -| AbstractCollection | remove | Object | -| AbstractCollection | removeAll | Collection | -| AbstractCollection | retainAll | Collection | -| AbstractCollection | toArray | T[] | | AbstractCollection | add | String | | AbstractCollection | addAll | Collection | | AbstractCollection | contains | Object | @@ -56,14 +32,6 @@ methodWithDuplicate | AbstractCollection | removeAll | Collection | | AbstractCollection | retainAll | Collection | | AbstractCollection | toArray | T[] | -| AbstractCollection | add | V | -| AbstractCollection | addAll | Collection | -| AbstractCollection | contains | Object | -| AbstractCollection | containsAll | Collection | -| AbstractCollection | remove | Object | -| AbstractCollection | removeAll | Collection | -| AbstractCollection | retainAll | Collection | -| AbstractCollection | toArray | T[] | | AbstractList | add | E | | AbstractList | add | int | | AbstractList | addAll | Collection | @@ -103,14 +71,14 @@ methodWithDuplicate | AbstractMap | put | V | | AbstractMap | putAll | Map | | AbstractMap | remove | Object | -| AbstractMap> | containsKey | Object | -| AbstractMap> | containsValue | Object | -| AbstractMap> | equals | Object | -| AbstractMap> | get | Object | -| AbstractMap> | put | Entry | -| AbstractMap> | put | Identity | -| AbstractMap> | putAll | Map> | -| AbstractMap> | remove | Object | +| AbstractMap | containsKey | Object | +| AbstractMap | containsValue | Object | +| AbstractMap | equals | Object | +| AbstractMap | get | Object | +| AbstractMap | put | Identity | +| AbstractMap | put | Object | +| AbstractMap | putAll | Map | +| AbstractMap | remove | Object | | AbstractMap | containsKey | Object | | AbstractMap | containsValue | Object | | AbstractMap | equals | Object | @@ -176,16 +144,6 @@ methodWithDuplicate | Collection | retainAll | Collection | | Collection | toArray | IntFunction | | Collection | toArray | T[] | -| Collection | add | Runnable | -| Collection | addAll | Collection | -| Collection | contains | Object | -| Collection | containsAll | Collection | -| Collection | remove | Object | -| Collection | removeAll | Collection | -| Collection | removeIf | Predicate | -| Collection | retainAll | Collection | -| Collection | toArray | IntFunction | -| Collection | toArray | T[] | | Collection | add | String | | Collection | addAll | Collection | | Collection | contains | Object | @@ -325,36 +283,35 @@ methodWithDuplicate | Map | replace | K | | Map | replace | V | | Map | replaceAll | BiFunction | -| Map> | compute | BiFunction,? extends Entry> | -| Map> | compute | Identity | -| Map> | computeIfAbsent | Function> | -| Map> | computeIfAbsent | Identity | -| Map> | computeIfPresent | BiFunction,? extends Entry> | -| Map> | computeIfPresent | Identity | -| Map> | containsKey | Object | -| Map> | containsValue | Object | -| Map> | copyOf | Map | -| Map> | entry | K | -| Map> | entry | V | -| Map> | forEach | BiConsumer> | -| Map> | get | Object | -| Map> | getOrDefault | Entry | -| Map> | getOrDefault | Object | -| Map> | merge | BiFunction,? super Entry,? extends Entry> | -| Map> | merge | Entry | -| Map> | merge | Identity | -| Map> | of | K | -| Map> | of | V | -| Map> | ofEntries | Entry[] | -| Map> | put | Entry | -| Map> | put | Identity | -| Map> | putAll | Map> | -| Map> | putIfAbsent | Entry | -| Map> | putIfAbsent | Identity | -| Map> | remove | Object | -| Map> | replace | Entry | -| Map> | replace | Identity | -| Map> | replaceAll | BiFunction,? extends Entry> | +| Map | compute | BiFunction | +| Map | compute | Identity | +| Map | computeIfAbsent | Function | +| Map | computeIfAbsent | Identity | +| Map | computeIfPresent | BiFunction | +| Map | computeIfPresent | Identity | +| Map | containsKey | Object | +| Map | containsValue | Object | +| Map | copyOf | Map | +| Map | entry | K | +| Map | entry | V | +| Map | forEach | BiConsumer | +| Map | get | Object | +| Map | getOrDefault | Object | +| Map | merge | BiFunction | +| Map | merge | Identity | +| Map | merge | Object | +| Map | of | K | +| Map | of | V | +| Map | ofEntries | Entry[] | +| Map | put | Identity | +| Map | put | Object | +| Map | putAll | Map | +| Map | putIfAbsent | Identity | +| Map | putIfAbsent | Object | +| Map | remove | Object | +| Map | replace | Identity | +| Map | replace | Object | +| Map | replaceAll | BiFunction | | Map | compute | BiFunction | | Map | compute | K | | Map | computeIfAbsent | Function | diff --git a/java/ql/test-kotlin2/library-tests/reflection/reflection.expected b/java/ql/test-kotlin2/library-tests/reflection/reflection.expected index 4074866da65f..b5de2b1adea3 100644 --- a/java/ql/test-kotlin2/library-tests/reflection/reflection.expected +++ b/java/ql/test-kotlin2/library-tests/reflection/reflection.expected @@ -329,6 +329,7 @@ compGenerated | file:///SignStyle.class:0:0:0:0 | getEntries | Default property accessor | | file:///StackWalker$ExtendedOption.class:0:0:0:0 | getEntries | Default property accessor | | file:///StackWalker$Option.class:0:0:0:0 | getEntries | Default property accessor | +| file:///String.class:0:0:0:0 | getChars | Forwarder for a Kotlin class inheriting an interface default method | | file:///String.class:0:0:0:0 | isEmpty | Forwarder for a Kotlin class inheriting an interface default method | | file:///TextStyle.class:0:0:0:0 | getEntries | Default property accessor | | file:///Thread$State.class:0:0:0:0 | getEntries | Default property accessor | diff --git a/java/ql/test/library-tests/dataflow/kdf/KDFDataflowTest.java b/java/ql/test/library-tests/dataflow/kdf/KDFDataflowTest.java new file mode 100644 index 000000000000..984cdd702a2b --- /dev/null +++ b/java/ql/test/library-tests/dataflow/kdf/KDFDataflowTest.java @@ -0,0 +1,74 @@ +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 + } +} \ No newline at end of file diff --git a/java/ql/test/library-tests/dataflow/kdf/options b/java/ql/test/library-tests/dataflow/kdf/options new file mode 100644 index 000000000000..f4edc64c0178 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/kdf/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args --enable-preview --release 25 \ No newline at end of file diff --git a/java/ql/test/library-tests/dataflow/kdf/test.expected b/java/ql/test/library-tests/dataflow/kdf/test.expected new file mode 100644 index 000000000000..f2e5841b7f38 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/kdf/test.expected @@ -0,0 +1,89 @@ +models +| 1 | Summary: java.lang; String; false; getBytes; ; ; Argument[this]; ReturnValue; taint; manual | +| 2 | Summary: javax.crypto.spec; HKDFParameterSpec$Builder; true; addIKM; (byte[]); ; Argument[0]; Argument[this]; taint; manual | +| 3 | Summary: javax.crypto.spec; HKDFParameterSpec$Builder; true; addIKM; (byte[]); ; Argument[this]; ReturnValue; value; manual | +| 4 | Summary: javax.crypto.spec; HKDFParameterSpec$Builder; true; thenExpand; (byte[],int); ; Argument[this]; ReturnValue; taint; manual | +| 5 | Summary: javax.crypto.spec; HKDFParameterSpec; false; expandOnly; (SecretKey,byte[],int); ; Argument[0]; ReturnValue; taint; manual | +| 6 | Summary: javax.crypto.spec; SecretKeySpec; false; SecretKeySpec; (byte[],String); ; Argument[0]; Argument[this]; taint; manual | +| 7 | Summary: javax.crypto; KDF; true; deriveData; (AlgorithmParameterSpec); ; Argument[0]; ReturnValue; taint; manual | +edges +| KDFDataflowTest.java:12:28:12:37 | source(...) : String | KDFDataflowTest.java:13:31:13:39 | userInput : String | provenance | | +| KDFDataflowTest.java:13:31:13:39 | userInput : String | KDFDataflowTest.java:13:31:13:50 | getBytes(...) : byte[] | provenance | MaD:1 | +| KDFDataflowTest.java:13:31:13:50 | getBytes(...) : byte[] | KDFDataflowTest.java:15:28:15:39 | taintedBytes : byte[] | provenance | | +| KDFDataflowTest.java:13:31:13:50 | getBytes(...) : byte[] | KDFDataflowTest.java:16:29:16:40 | taintedBytes : byte[] | provenance | | +| KDFDataflowTest.java:13:31:13:50 | getBytes(...) : byte[] | KDFDataflowTest.java:17:25:17:36 | taintedBytes : byte[] | provenance | | +| KDFDataflowTest.java:13:31:13:50 | getBytes(...) : byte[] | KDFDataflowTest.java:18:33:18:44 | taintedBytes : byte[] | provenance | | +| KDFDataflowTest.java:15:28:15:39 | taintedBytes : byte[] | KDFDataflowTest.java:22:43:22:59 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:16:29:16:40 | taintedBytes : byte[] | KDFDataflowTest.java:32:44:32:60 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:17:25:17:36 | taintedBytes : byte[] | KDFDataflowTest.java:42:40:42:56 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:18:33:18:44 | taintedBytes : byte[] | KDFDataflowTest.java:53:48:53:64 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:22:43:22:59 | taintedIKM : byte[] | KDFDataflowTest.java:24:24:24:33 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:24:9:24:15 | builder [post update] : Builder | KDFDataflowTest.java:25:34:25:40 | builder : Builder | provenance | | +| KDFDataflowTest.java:24:24:24:33 | taintedIKM : byte[] | KDFDataflowTest.java:24:9:24:15 | builder [post update] : Builder | provenance | MaD:2 | +| KDFDataflowTest.java:25:34:25:40 | builder : Builder | KDFDataflowTest.java:25:34:25:74 | thenExpand(...) : ExtractThenExpand | provenance | MaD:4 | +| KDFDataflowTest.java:25:34:25:74 | thenExpand(...) : ExtractThenExpand | KDFDataflowTest.java:28:40:28:43 | spec : ExtractThenExpand | provenance | | +| KDFDataflowTest.java:28:25:28:44 | deriveData(...) : byte[] | KDFDataflowTest.java:29:14:29:19 | result | provenance | | +| KDFDataflowTest.java:28:40:28:43 | spec : ExtractThenExpand | KDFDataflowTest.java:28:25:28:44 | deriveData(...) : byte[] | provenance | MaD:7 | +| KDFDataflowTest.java:32:44:32:60 | taintedIKM : byte[] | KDFDataflowTest.java:34:62:34:71 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:34:46:34:72 | addIKM(...) : Builder | KDFDataflowTest.java:35:34:35:41 | builder2 : Builder | provenance | | +| KDFDataflowTest.java:34:62:34:71 | taintedIKM : byte[] | KDFDataflowTest.java:34:46:34:72 | addIKM(...) : Builder | provenance | MaD:2+MaD:3 | +| KDFDataflowTest.java:35:34:35:41 | builder2 : Builder | KDFDataflowTest.java:35:34:35:75 | thenExpand(...) : ExtractThenExpand | provenance | MaD:4 | +| KDFDataflowTest.java:35:34:35:75 | thenExpand(...) : ExtractThenExpand | KDFDataflowTest.java:38:40:38:43 | spec : ExtractThenExpand | provenance | | +| KDFDataflowTest.java:38:25:38:44 | deriveData(...) : byte[] | KDFDataflowTest.java:39:14:39:19 | result | provenance | | +| KDFDataflowTest.java:38:40:38:43 | spec : ExtractThenExpand | KDFDataflowTest.java:38:25:38:44 | deriveData(...) : byte[] | provenance | MaD:7 | +| KDFDataflowTest.java:42:40:42:56 | taintedIKM : byte[] | KDFDataflowTest.java:44:24:44:33 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:44:9:44:15 | builder [post update] : Builder | KDFDataflowTest.java:46:34:46:40 | builder : Builder | provenance | | +| KDFDataflowTest.java:44:24:44:33 | taintedIKM : byte[] | KDFDataflowTest.java:44:9:44:15 | builder [post update] : Builder | provenance | MaD:2 | +| KDFDataflowTest.java:46:34:46:40 | builder : Builder | KDFDataflowTest.java:46:34:46:74 | thenExpand(...) : ExtractThenExpand | provenance | MaD:4 | +| KDFDataflowTest.java:46:34:46:74 | thenExpand(...) : ExtractThenExpand | KDFDataflowTest.java:49:40:49:43 | spec : ExtractThenExpand | provenance | | +| KDFDataflowTest.java:49:25:49:44 | deriveData(...) : byte[] | KDFDataflowTest.java:50:14:50:19 | result | provenance | | +| KDFDataflowTest.java:49:40:49:43 | spec : ExtractThenExpand | KDFDataflowTest.java:49:25:49:44 | deriveData(...) : byte[] | provenance | MaD:7 | +| KDFDataflowTest.java:53:48:53:64 | taintedIKM : byte[] | KDFDataflowTest.java:54:89:54:98 | taintedIKM : byte[] | provenance | | +| KDFDataflowTest.java:54:53:54:106 | new SecretKeySpec(...) : SecretKeySpec | KDFDataflowTest.java:56:13:56:21 | secretKey : SecretKeySpec | provenance | | +| KDFDataflowTest.java:54:89:54:98 | taintedIKM : byte[] | KDFDataflowTest.java:54:53:54:106 | new SecretKeySpec(...) : SecretKeySpec | provenance | MaD:6 | +| KDFDataflowTest.java:55:34:56:45 | expandOnly(...) : Expand | KDFDataflowTest.java:59:40:59:43 | spec : Expand | provenance | | +| KDFDataflowTest.java:56:13:56:21 | secretKey : SecretKeySpec | KDFDataflowTest.java:55:34:56:45 | expandOnly(...) : Expand | provenance | MaD:5 | +| KDFDataflowTest.java:59:25:59:44 | deriveData(...) : byte[] | KDFDataflowTest.java:60:14:60:19 | result | provenance | | +| KDFDataflowTest.java:59:40:59:43 | spec : Expand | KDFDataflowTest.java:59:25:59:44 | deriveData(...) : byte[] | provenance | MaD:7 | +nodes +| KDFDataflowTest.java:12:28:12:37 | source(...) : String | semmle.label | source(...) : String | +| KDFDataflowTest.java:13:31:13:39 | userInput : String | semmle.label | userInput : String | +| KDFDataflowTest.java:13:31:13:50 | getBytes(...) : byte[] | semmle.label | getBytes(...) : byte[] | +| KDFDataflowTest.java:15:28:15:39 | taintedBytes : byte[] | semmle.label | taintedBytes : byte[] | +| KDFDataflowTest.java:16:29:16:40 | taintedBytes : byte[] | semmle.label | taintedBytes : byte[] | +| KDFDataflowTest.java:17:25:17:36 | taintedBytes : byte[] | semmle.label | taintedBytes : byte[] | +| KDFDataflowTest.java:18:33:18:44 | taintedBytes : byte[] | semmle.label | taintedBytes : byte[] | +| KDFDataflowTest.java:22:43:22:59 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:24:9:24:15 | builder [post update] : Builder | semmle.label | builder [post update] : Builder | +| KDFDataflowTest.java:24:24:24:33 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:25:34:25:40 | builder : Builder | semmle.label | builder : Builder | +| KDFDataflowTest.java:25:34:25:74 | thenExpand(...) : ExtractThenExpand | semmle.label | thenExpand(...) : ExtractThenExpand | +| KDFDataflowTest.java:28:25:28:44 | deriveData(...) : byte[] | semmle.label | deriveData(...) : byte[] | +| KDFDataflowTest.java:28:40:28:43 | spec : ExtractThenExpand | semmle.label | spec : ExtractThenExpand | +| KDFDataflowTest.java:29:14:29:19 | result | semmle.label | result | +| KDFDataflowTest.java:32:44:32:60 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:34:46:34:72 | addIKM(...) : Builder | semmle.label | addIKM(...) : Builder | +| KDFDataflowTest.java:34:62:34:71 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:35:34:35:41 | builder2 : Builder | semmle.label | builder2 : Builder | +| KDFDataflowTest.java:35:34:35:75 | thenExpand(...) : ExtractThenExpand | semmle.label | thenExpand(...) : ExtractThenExpand | +| KDFDataflowTest.java:38:25:38:44 | deriveData(...) : byte[] | semmle.label | deriveData(...) : byte[] | +| KDFDataflowTest.java:38:40:38:43 | spec : ExtractThenExpand | semmle.label | spec : ExtractThenExpand | +| KDFDataflowTest.java:39:14:39:19 | result | semmle.label | result | +| KDFDataflowTest.java:42:40:42:56 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:44:9:44:15 | builder [post update] : Builder | semmle.label | builder [post update] : Builder | +| KDFDataflowTest.java:44:24:44:33 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:46:34:46:40 | builder : Builder | semmle.label | builder : Builder | +| KDFDataflowTest.java:46:34:46:74 | thenExpand(...) : ExtractThenExpand | semmle.label | thenExpand(...) : ExtractThenExpand | +| KDFDataflowTest.java:49:25:49:44 | deriveData(...) : byte[] | semmle.label | deriveData(...) : byte[] | +| KDFDataflowTest.java:49:40:49:43 | spec : ExtractThenExpand | semmle.label | spec : ExtractThenExpand | +| KDFDataflowTest.java:50:14:50:19 | result | semmle.label | result | +| KDFDataflowTest.java:53:48:53:64 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:54:53:54:106 | new SecretKeySpec(...) : SecretKeySpec | semmle.label | new SecretKeySpec(...) : SecretKeySpec | +| KDFDataflowTest.java:54:89:54:98 | taintedIKM : byte[] | semmle.label | taintedIKM : byte[] | +| KDFDataflowTest.java:55:34:56:45 | expandOnly(...) : Expand | semmle.label | expandOnly(...) : Expand | +| KDFDataflowTest.java:56:13:56:21 | secretKey : SecretKeySpec | semmle.label | secretKey : SecretKeySpec | +| KDFDataflowTest.java:59:25:59:44 | deriveData(...) : byte[] | semmle.label | deriveData(...) : byte[] | +| KDFDataflowTest.java:59:40:59:43 | spec : Expand | semmle.label | spec : Expand | +| KDFDataflowTest.java:60:14:60:19 | result | semmle.label | result | +subpaths +testFailures diff --git a/java/ql/test/library-tests/dataflow/kdf/test.ql b/java/ql/test/library-tests/dataflow/kdf/test.ql new file mode 100644 index 000000000000..b2ffe7d9a679 --- /dev/null +++ b/java/ql/test/library-tests/dataflow/kdf/test.ql @@ -0,0 +1,3 @@ +import utils.test.InlineFlowTest +import TaintFlowTest +import TaintFlow::PathGraph