Skip to content

Commit 2bc2649

Browse files
Merge pull request #9853 from joefarebrother/static-init-vec
Java: Promote Static Initialization Vector query
2 parents 61db581 + 7c188a6 commit 2bc2649

File tree

10 files changed

+73
-70
lines changed

10 files changed

+73
-70
lines changed

java/ql/src/experimental/semmle/code/java/security/StaticInitializationVectorQuery.qll renamed to java/ql/lib/semmle/code/java/security/StaticInitializationVectorQuery.qll

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/** Definitions for the Static Initialization Vector query. */
2+
13
import java
24
import semmle.code.java.dataflow.TaintTracking
35
import semmle.code.java.dataflow.TaintTracking2
@@ -9,15 +11,14 @@ private predicate initializedWithConstants(ArrayCreationExpr array) {
911
// creating an array without an initializer, for example `new byte[8]`
1012
not exists(array.getInit())
1113
or
12-
// creating a multidimensional array with an initializer like `{ new byte[8], new byte[16] }`
13-
// This works around https://github.com/github/codeql/issues/6552 -- change me once there is
14-
// a better way to distinguish nested initializers that create zero-filled arrays
15-
// (e.g. `new byte[1]`) from those with an initializer list (`new byte[] { 1 }` or just `{ 1 }`)
16-
array.getInit().getAnInit().getAChildExpr() instanceof IntegerLiteral
17-
or
18-
// creating an array wit an initializer like `new byte[] { 1, 2 }`
19-
forex(Expr element | element = array.getInit().getAnInit() |
14+
initializedWithConstantsHelper(array.getInit())
15+
}
16+
17+
private predicate initializedWithConstantsHelper(ArrayInit arInit) {
18+
forex(Expr element | element = arInit.getAnInit() |
2019
element instanceof CompileTimeConstantExpr
20+
or
21+
initializedWithConstantsHelper(element)
2122
)
2223
}
2324

@@ -74,9 +75,7 @@ private class ArrayUpdateConfig extends TaintTracking2::Configuration {
7475
source.asExpr() instanceof StaticByteArrayCreation
7576
}
7677

77-
override predicate isSink(DataFlow::Node sink) {
78-
exists(ArrayUpdate update | update.getArray() = sink.asExpr())
79-
}
78+
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(ArrayUpdate upd).getArray() }
8079
}
8180

8281
/**
@@ -85,29 +84,12 @@ private class ArrayUpdateConfig extends TaintTracking2::Configuration {
8584
private class StaticInitializationVectorSource extends DataFlow::Node {
8685
StaticInitializationVectorSource() {
8786
exists(StaticByteArrayCreation array | array = this.asExpr() |
88-
not exists(ArrayUpdateConfig config | config.hasFlow(DataFlow2::exprNode(array), _))
89-
)
90-
}
91-
}
92-
93-
/**
94-
* A config that tracks initialization of a cipher for encryption.
95-
*/
96-
private class EncryptionModeConfig extends TaintTracking2::Configuration {
97-
EncryptionModeConfig() { this = "EncryptionModeConfig" }
98-
99-
override predicate isSource(DataFlow::Node source) {
100-
source
101-
.asExpr()
102-
.(FieldRead)
103-
.getField()
104-
.hasQualifiedName("javax.crypto", "Cipher", "ENCRYPT_MODE")
105-
}
106-
107-
override predicate isSink(DataFlow::Node sink) {
108-
exists(MethodAccess ma, Method m | m = ma.getMethod() |
109-
m.hasQualifiedName("javax.crypto", "Cipher", "init") and
110-
ma.getArgument(0) = sink.asExpr()
87+
not exists(ArrayUpdateConfig config | config.hasFlow(DataFlow2::exprNode(array), _)) and
88+
// Reduce FPs from utility methods that return an empty array in an exceptional case
89+
not exists(ReturnStmt ret |
90+
array.getADimension().(CompileTimeConstantExpr).getIntValue() = 0 and
91+
DataFlow::localExprFlow(array, ret.getResult())
92+
)
11193
)
11294
}
11395
}
@@ -117,13 +99,14 @@ private class EncryptionModeConfig extends TaintTracking2::Configuration {
11799
*/
118100
private class EncryptionInitializationSink extends DataFlow::Node {
119101
EncryptionInitializationSink() {
120-
exists(MethodAccess ma, Method m, EncryptionModeConfig config | m = ma.getMethod() |
102+
exists(MethodAccess ma, Method m, FieldRead fr | m = ma.getMethod() |
121103
m.hasQualifiedName("javax.crypto", "Cipher", "init") and
122104
m.getParameterType(2)
123105
.(RefType)
124106
.hasQualifiedName("java.security.spec", "AlgorithmParameterSpec") and
125-
ma.getArgument(2) = this.asExpr() and
126-
config.hasFlowToExpr(ma.getArgument(0))
107+
fr.getField().hasQualifiedName("javax.crypto", "Cipher", "ENCRYPT_MODE") and
108+
DataFlow::localExprFlow(fr, ma.getArgument(0)) and
109+
ma.getArgument(2) = this.asExpr()
127110
)
128111
}
129112
}

java/ql/src/experimental/Security/CWE/CWE-1204/StaticInitializationVector.qhelp renamed to java/ql/src/Security/CWE/CWE-1204/StaticInitializationVector.qhelp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33

44
<overview>
55
<p>
6-
A cipher needs an initialization vector (IV) when it is used in certain modes
7-
such as CBC or GCM. Under the same secret key, IVs should be unique and ideally unpredictable.
8-
Given a secret key, if the same IV is used for encryption, the same plaintexts result in the same ciphertexts.
9-
This lets an attacker learn if the same data pieces are transferred or stored,
10-
or this can help the attacker run a dictionary attack.
6+
When a cipher is used in certain modes such as CBC or GCM, it requires an initialization vector (IV).
7+
Under the same secret key, IVs should be unique and ideally unpredictable.
8+
If the same IV is used with the same secret key, then the same plaintext results in the same ciphertext.
9+
This can let an attacker learn if the same data pieces are transferred or stored, or help the attacker run a dictionary attack.
1110
</p>
1211
</overview>
1312

@@ -19,7 +18,7 @@ Use a random IV generated by <code>SecureRandom</code>.
1918

2019
<example>
2120
<p>
22-
The following example initializes a cipher with a static IV which is unsafe:
21+
The following example initializes a cipher with a static IV, which is unsafe:
2322
</p>
2423
<sample src="BadStaticInitializationVector.java" />
2524

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name Using a static initialization vector for encryption
3+
* @description An initialization vector (IV) used for ciphers of certain modes (such as CBC or GCM) should be unique and unpredictable, to maximize encryption and prevent dictionary attacks.
4+
* @kind path-problem
5+
* @problem.severity warning
6+
* @security-severity 7.5
7+
* @precision high
8+
* @id java/static-initialization-vector
9+
* @tags security
10+
* external/cwe/cwe-329
11+
* external/cwe/cwe-1204
12+
*/
13+
14+
import java
15+
import semmle.code.java.security.StaticInitializationVectorQuery
16+
import DataFlow::PathGraph
17+
18+
from DataFlow::PathNode source, DataFlow::PathNode sink, StaticInitializationVectorConfig conf
19+
where conf.hasFlowPath(source, sink)
20+
select sink.getNode(), source, sink, "A $@ should not be used for encryption.", source.getNode(),
21+
"static initialization vector"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* The query "Using a static initialization vector for encryption" (`java/static-initialization-vector`) has been promoted from experimental to the main query pack. This query was originally [submitted as an experimental query by @artem-smotrakov](https://github.com/github/codeql/pull/6357).

java/ql/src/experimental/Security/CWE/CWE-1204/StaticInitializationVector.ql

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,26 @@ public byte[] encryptWithRandomIvWithArraysCopy(byte[] key, byte[] plaintext) th
164164
cipher.update(plaintext);
165165
return cipher.doFinal();
166166
}
167+
168+
public byte[] generate(int size) throws Exception {
169+
if (size == 0) {
170+
return new byte[0];
171+
}
172+
byte[] randomBytes = new byte[size];
173+
SecureRandom.getInstanceStrong().nextBytes(randomBytes);
174+
return randomBytes;
175+
}
176+
177+
// GOOD: AES-CBC with a random IV
178+
public byte[] encryptWithGeneratedIvByteArray(byte[] key, byte[] plaintext) throws Exception {
179+
byte[] iv = generate(16);
180+
181+
IvParameterSpec ivSpec = new IvParameterSpec(iv);
182+
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
183+
184+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
185+
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
186+
cipher.update(plaintext);
187+
return cipher.doFinal();
188+
}
167189
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import java
2-
import experimental.semmle.code.java.security.StaticInitializationVectorQuery
2+
import semmle.code.java.security.StaticInitializationVectorQuery
33
import TestUtilities.InlineExpectationsTest
44

55
class StaticInitializationVectorTest extends InlineExpectationsTest {

0 commit comments

Comments
 (0)