|
17 | 17 | */
|
18 | 18 |
|
19 | 19 | import java
|
20 |
| -import semmle.code.java.dataflow.TaintTracking |
21 |
| -import semmle.code.java.dataflow.TaintTracking2 |
| 20 | +import experimental.semmle.code.java.security.StaticInitializationVectorQuery |
22 | 21 | import DataFlow::PathGraph
|
23 | 22 |
|
24 |
| -/** |
25 |
| - * Holds if `array` is initialized only with constants, for example, |
26 |
| - * `new byte[8]` or `new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }`. |
27 |
| - */ |
28 |
| -private predicate initializedWithConstants(ArrayCreationExpr array) { |
29 |
| - not exists(array.getInit()) |
30 |
| - or |
31 |
| - forex(Expr element | element = array.getInit().getAChildExpr() | |
32 |
| - element instanceof CompileTimeConstantExpr |
33 |
| - ) |
34 |
| -} |
35 |
| - |
36 |
| -/** |
37 |
| - * An expression that creates a byte array that is initialized with constants. |
38 |
| - */ |
39 |
| -private class StaticByteArrayCreation extends ArrayCreationExpr { |
40 |
| - StaticByteArrayCreation() { |
41 |
| - this.getType().(Array).getElementType().(PrimitiveType).getName() = "byte" and |
42 |
| - initializedWithConstants(this) |
43 |
| - } |
44 |
| -} |
45 |
| - |
46 |
| -/** Defines a sub-set of expressions that update an array. */ |
47 |
| -private class ArrayUpdate extends Expr { |
48 |
| - Expr array; |
49 |
| - |
50 |
| - ArrayUpdate() { |
51 |
| - exists(Assignment assign, ArrayAccess arrayAccess | arrayAccess = assign.getDest() | |
52 |
| - assign = this and |
53 |
| - arrayAccess.getArray() = array and |
54 |
| - not assign.getSource() instanceof CompileTimeConstantExpr |
55 |
| - ) |
56 |
| - or |
57 |
| - exists(StaticMethodAccess ma | |
58 |
| - ma.getMethod().hasQualifiedName("java.lang", "System", "arraycopy") and |
59 |
| - ma = this and |
60 |
| - ma.getArgument(2) = array |
61 |
| - ) |
62 |
| - or |
63 |
| - exists(StaticMethodAccess ma | |
64 |
| - ma.getMethod().hasQualifiedName("java.util", "Arrays", "copyOf") and |
65 |
| - ma = this and |
66 |
| - ma = array |
67 |
| - ) |
68 |
| - or |
69 |
| - exists(MethodAccess ma, Method m | |
70 |
| - m = ma.getMethod() and |
71 |
| - ma = this and |
72 |
| - ma.getArgument(0) = array |
73 |
| - | |
74 |
| - m.hasQualifiedName("java.io", "InputStream", "read") or |
75 |
| - m.hasQualifiedName("java.nio", "ByteBuffer", "get") or |
76 |
| - m.hasQualifiedName("java.security", "SecureRandom", "nextBytes") or |
77 |
| - m.hasQualifiedName("java.util", "Random", "nextBytes") |
78 |
| - ) |
79 |
| - } |
80 |
| - |
81 |
| - /** Returns the updated array. */ |
82 |
| - Expr getArray() { result = array } |
83 |
| -} |
84 |
| - |
85 |
| -/** |
86 |
| - * A config that tracks dataflow from creating an array to an operation that updates it. |
87 |
| - */ |
88 |
| -private class ArrayUpdateConfig extends TaintTracking2::Configuration { |
89 |
| - ArrayUpdateConfig() { this = "ArrayUpdateConfig" } |
90 |
| - |
91 |
| - override predicate isSource(DataFlow::Node source) { |
92 |
| - source.asExpr() instanceof StaticByteArrayCreation |
93 |
| - } |
94 |
| - |
95 |
| - override predicate isSink(DataFlow::Node sink) { |
96 |
| - exists(ArrayUpdate update | update.getArray() = sink.asExpr()) |
97 |
| - } |
98 |
| -} |
99 |
| - |
100 |
| -/** |
101 |
| - * A source that defines an array that doesn't get updated. |
102 |
| - */ |
103 |
| -private class StaticInitializationVectorSource extends DataFlow::Node { |
104 |
| - StaticInitializationVectorSource() { |
105 |
| - exists(StaticByteArrayCreation array | array = this.asExpr() | |
106 |
| - not exists(ArrayUpdate update, ArrayUpdateConfig config | |
107 |
| - config.hasFlow(DataFlow2::exprNode(array), DataFlow2::exprNode(update.getArray())) |
108 |
| - ) |
109 |
| - ) |
110 |
| - } |
111 |
| -} |
112 |
| - |
113 |
| -/** |
114 |
| - * A config that tracks initialization of a cipher for encryption. |
115 |
| - */ |
116 |
| -private class EncryptionModeConfig extends TaintTracking2::Configuration { |
117 |
| - EncryptionModeConfig() { this = "EncryptionModeConfig" } |
118 |
| - |
119 |
| - override predicate isSource(DataFlow::Node source) { |
120 |
| - source.asExpr().(VarAccess).getVariable().hasName("ENCRYPT_MODE") |
121 |
| - } |
122 |
| - |
123 |
| - override predicate isSink(DataFlow::Node sink) { |
124 |
| - exists(MethodAccess ma, Method m | m = ma.getMethod() | |
125 |
| - m.hasQualifiedName("javax.crypto", "Cipher", "init") and |
126 |
| - ma.getArgument(0) = sink.asExpr() |
127 |
| - ) |
128 |
| - } |
129 |
| -} |
130 |
| - |
131 |
| -/** |
132 |
| - * A sink that initializes a cipher for encryption with unsafe parameters. |
133 |
| - */ |
134 |
| -private class EncryptionInitializationSink extends DataFlow::Node { |
135 |
| - EncryptionInitializationSink() { |
136 |
| - exists(MethodAccess ma, Method m, EncryptionModeConfig config | m = ma.getMethod() | |
137 |
| - m.hasQualifiedName("javax.crypto", "Cipher", "init") and |
138 |
| - m.getParameterType(2) |
139 |
| - .(RefType) |
140 |
| - .hasQualifiedName("java.security.spec", "AlgorithmParameterSpec") and |
141 |
| - ma.getArgument(2) = this.asExpr() and |
142 |
| - config.hasFlowToExpr(ma.getArgument(0)) |
143 |
| - ) |
144 |
| - } |
145 |
| -} |
146 |
| - |
147 |
| -/** |
148 |
| - * Holds if `fromNode` to `toNode` is a dataflow step |
149 |
| - * that creates cipher's parameters with initialization vector. |
150 |
| - */ |
151 |
| -private predicate createInitializationVectorSpecStep(DataFlow::Node fromNode, DataFlow::Node toNode) { |
152 |
| - exists(ConstructorCall cc, RefType type | |
153 |
| - cc = toNode.asExpr() and type = cc.getConstructedType() |
154 |
| - | |
155 |
| - type.hasQualifiedName("javax.crypto.spec", "IvParameterSpec") and |
156 |
| - cc.getArgument(0) = fromNode.asExpr() |
157 |
| - or |
158 |
| - type.hasQualifiedName("javax.crypto.spec", ["GCMParameterSpec", "RC2ParameterSpec"]) and |
159 |
| - cc.getArgument(1) = fromNode.asExpr() |
160 |
| - or |
161 |
| - type.hasQualifiedName("javax.crypto.spec", "RC5ParameterSpec") and |
162 |
| - cc.getArgument(3) = fromNode.asExpr() |
163 |
| - ) |
164 |
| -} |
165 |
| - |
166 |
| -/** |
167 |
| - * A config that tracks dataflow to initializing a cipher with a static initialization vector. |
168 |
| - */ |
169 |
| -private class StaticInitializationVectorConfig extends TaintTracking::Configuration { |
170 |
| - StaticInitializationVectorConfig() { this = "StaticInitializationVectorConfig" } |
171 |
| - |
172 |
| - override predicate isSource(DataFlow::Node source) { |
173 |
| - source instanceof StaticInitializationVectorSource |
174 |
| - } |
175 |
| - |
176 |
| - override predicate isSink(DataFlow::Node sink) { sink instanceof EncryptionInitializationSink } |
177 |
| - |
178 |
| - override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { |
179 |
| - createInitializationVectorSpecStep(fromNode, toNode) |
180 |
| - } |
181 |
| - |
182 |
| - override predicate isSanitizer(DataFlow::Node node) { |
183 |
| - exists(ArrayUpdate update | update.getArray() = node.asExpr()) |
184 |
| - } |
185 |
| -} |
186 |
| - |
187 | 23 | from DataFlow::PathNode source, DataFlow::PathNode sink, StaticInitializationVectorConfig conf
|
188 | 24 | where conf.hasFlowPath(source, sink)
|
189 | 25 | select sink.getNode(), source, sink, "A $@ should not be used for encryption.", source.getNode(),
|
|
0 commit comments