@@ -58,8 +58,12 @@ module JCAModel {
5858 Expr getProviderArg ( ) { result = this .getArgument ( 1 ) }
5959 }
6060
61- class CipherDoFinalCall extends Call {
62- CipherDoFinalCall ( ) { this .getCallee ( ) .hasQualifiedName ( "javax.crypto" , "Cipher" , "doFinal" ) }
61+ private class JCACipherOperationCall extends Call {
62+ JCACipherOperationCall ( ) {
63+ exists ( string s | s in [ "doFinal" , "wrap" , "unwrap" ] |
64+ this .getCallee ( ) .hasQualifiedName ( "javax.crypto" , "Cipher" , s )
65+ )
66+ }
6367 }
6468
6569 /**
@@ -81,7 +85,7 @@ module JCAModel {
8185 * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`.
8286 */
8387 class CipherGetInstanceAlgorithmArg extends Crypto:: EncryptionAlgorithmInstance ,
84- Crypto:: ModeOfOperationAlgorithmInstance , Crypto:: PaddingAlgorithmInstance instanceof Expr
88+ Crypto:: BlockCipherModeOfOperationAlgorithmInstance , Crypto:: PaddingAlgorithmInstance instanceof Expr
8589 {
8690 CipherGetInstanceCall call ;
8791
@@ -98,68 +102,136 @@ module JCAModel {
98102 CipherGetInstanceCall getCall ( ) { result = call }
99103 }
100104
101- // TODO: what if encrypt/decrypt mode isn't known
102- private module CipherGetInstanceToFinalizeConfig implements DataFlow:: StateConfigSig {
103- class FlowState = Crypto:: TCipherOperationMode ;
105+ /**
106+ * An access to the `javax.crypto.Cipher` class.
107+ */
108+ private class CipherAccess extends TypeAccess {
109+ CipherAccess ( ) { this .getType ( ) .( Class ) .hasQualifiedName ( "javax.crypto" , "Cipher" ) }
110+ }
111+
112+ /**
113+ * An access to a cipher mode field of the `javax.crypto.Cipher` class,
114+ * specifically `ENCRYPT_MODE`, `DECRYPT_MODE`, `WRAP_MODE`, or `UNWRAP_MODE`.
115+ */
116+ private class JavaxCryptoCipherOperationModeAccess extends FieldAccess {
117+ JavaxCryptoCipherOperationModeAccess ( ) {
118+ this .getQualifier ( ) instanceof CipherAccess and
119+ this .getField ( ) .getName ( ) in [ "ENCRYPT_MODE" , "DECRYPT_MODE" , "WRAP_MODE" , "UNWRAP_MODE" ]
120+ }
121+ }
122+
123+ private newtype TCipherModeFlowState =
124+ TUninitializedCipherModeFlowState ( ) or
125+ TInitializedCipherModeFlowState ( CipherInitCall call )
126+
127+ abstract private class CipherModeFlowState extends TCipherModeFlowState {
128+ string toString ( ) {
129+ this = TUninitializedCipherModeFlowState ( ) and result = "uninitialized"
130+ or
131+ this = TInitializedCipherModeFlowState ( _) and result = "initialized"
132+ }
133+
134+ abstract Crypto:: CipherOperationMode getCipherOperationMode ( ) ;
135+ }
136+
137+ private class UninitializedCipherModeFlowState extends CipherModeFlowState ,
138+ TUninitializedCipherModeFlowState
139+ {
140+ override Crypto:: CipherOperationMode getCipherOperationMode ( ) {
141+ result instanceof Crypto:: UnknownCipherOperationMode
142+ }
143+ }
144+
145+ private class InitializedCipherModeFlowState extends CipherModeFlowState ,
146+ TInitializedCipherModeFlowState
147+ {
148+ CipherInitCall call ;
149+ DataFlow:: Node node1 ;
150+ DataFlow:: Node node2 ;
151+ Crypto:: CipherOperationMode mode ;
152+
153+ InitializedCipherModeFlowState ( ) {
154+ this = TInitializedCipherModeFlowState ( call ) and
155+ DataFlow:: localFlowStep ( node1 , node2 ) and
156+ node2 .asExpr ( ) = call .getQualifier ( ) and
157+ // I would imagine this would make this predicate horribly horribly inefficient
158+ // it now binds with anything
159+ not node1 .asExpr ( ) = call .getQualifier ( ) and
160+ mode = call .getCipherOperationModeType ( )
161+ }
162+
163+ CipherInitCall getCall ( ) { result = call }
164+
165+ DataFlow:: Node getFstNode ( ) { result = node1 }
166+
167+ /**
168+ * Returns the node *to* which the state-changing step occurs
169+ */
170+ DataFlow:: Node getSndNode ( ) { result = node2 }
171+
172+ override Crypto:: CipherOperationMode getCipherOperationMode ( ) { result = mode }
173+ }
174+
175+ /**
176+ * Trace to a cryptographic operation,
177+ * specifically `Cipher.doFinal()`, `Cipher.wrap()`, or `Cipher.unwrap()`.
178+ */
179+ private module CipherGetInstanceToCipherOperationConfig implements DataFlow:: StateConfigSig {
180+ class FlowState = TCipherModeFlowState ;
104181
105182 predicate isSource ( DataFlow:: Node src , FlowState state ) {
106- state = Crypto :: UnknownCipherOperationMode ( ) and
183+ state instanceof UninitializedCipherModeFlowState and
107184 src .asExpr ( ) instanceof CipherGetInstanceCall
108185 }
109186
110- predicate isSink ( DataFlow:: Node sink , FlowState state ) {
111- exists ( CipherDoFinalCall c | c .getQualifier ( ) = sink .asExpr ( ) )
187+ predicate isSink ( DataFlow:: Node sink , FlowState state ) { none ( ) }
188+
189+ predicate isSink ( DataFlow:: Node sink ) {
190+ exists ( JCACipherOperationCall c | c .getQualifier ( ) = sink .asExpr ( ) )
112191 }
113192
114193 predicate isAdditionalFlowStep (
115194 DataFlow:: Node node1 , FlowState state1 , DataFlow:: Node node2 , FlowState state2
116195 ) {
117- exists ( CipherInitCall c |
118- c .getQualifier ( ) = node1 .asExpr ( ) and
119- // TODO: not taking into consideration if the mode traces to this arg
120- exists ( FieldAccess fa |
121- c .getModeArg ( ) = fa and
122- (
123- if fa .getField ( ) .getName ( ) in [ "ENCRYPT_MODE" , "WRAP_MODE" ]
124- then state2 = Crypto:: EncryptionMode ( )
125- else (
126- if fa .getField ( ) .getName ( ) in [ "DECRYPT_MODE" , "UNWRAP_MODE" ]
127- then state2 = Crypto:: DecryptionMode ( )
128- else state2 = Crypto:: UnknownCipherOperationMode ( )
129- )
130- )
131- )
132- ) and
133- node2 = node1
196+ node1 = state2 .( InitializedCipherModeFlowState ) .getFstNode ( ) and
197+ node2 = state2 .( InitializedCipherModeFlowState ) .getSndNode ( )
198+ }
199+
200+ predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
201+ exists ( CipherInitCall call | node .asExpr ( ) = call .getQualifier ( ) |
202+ state instanceof UninitializedCipherModeFlowState
203+ or
204+ state .( InitializedCipherModeFlowState ) .getCall ( ) != call
205+ )
134206 }
135207 }
136208
137- module CipherGetInstanceToFinalizeFlow =
138- DataFlow:: GlobalWithState< CipherGetInstanceToFinalizeConfig > ;
209+ module CipherGetInstanceToCipherOperationFlow =
210+ DataFlow:: GlobalWithState< CipherGetInstanceToCipherOperationConfig > ;
139211
140212 class CipherEncryptionOperation extends Crypto:: CipherOperationInstance instanceof Call {
141- Crypto:: TCipherOperationMode mode ;
213+ Crypto:: CipherOperationMode mode ;
142214 Crypto:: EncryptionAlgorithmInstance algorithm ;
143215
144216 CipherEncryptionOperation ( ) {
145217 exists (
146- CipherGetInstanceToFinalizeFlow :: PathNode sink ,
147- CipherGetInstanceToFinalizeFlow :: PathNode src , CipherGetInstanceCall getCipher ,
148- CipherDoFinalCall doFinalize , CipherGetInstanceAlgorithmArg arg
218+ CipherGetInstanceToCipherOperationFlow :: PathNode sink ,
219+ CipherGetInstanceToCipherOperationFlow :: PathNode src , CipherGetInstanceCall getCipher ,
220+ JCACipherOperationCall doFinalize , CipherGetInstanceAlgorithmArg arg
149221 |
150- CipherGetInstanceToFinalizeFlow :: flowPath ( src , sink ) and
222+ CipherGetInstanceToCipherOperationFlow :: flowPath ( src , sink ) and
151223 src .getNode ( ) .asExpr ( ) = getCipher and
152224 sink .getNode ( ) .asExpr ( ) = doFinalize .getQualifier ( ) and
153- sink .getState ( ) = mode and
225+ sink .getState ( ) . ( CipherModeFlowState ) . getCipherOperationMode ( ) = mode and
154226 this = doFinalize and
155- arg .getCall ( ) = getCipher and
227+ arg .getCall ( ) = getCipher and
156228 algorithm = arg
157229 )
158230 }
159231
160232 override Crypto:: EncryptionAlgorithmInstance getAlgorithm ( ) { result = algorithm }
161233
162- override Crypto:: TCipherOperationMode getCipherOperationMode ( ) { result = mode }
234+ override Crypto:: CipherOperationMode getCipherOperationMode ( ) { result = mode }
163235 }
164236
165237 /**
@@ -177,7 +249,7 @@ module JCAModel {
177249 CipherGetInstanceAlgorithmArg instance ;
178250
179251 ModeOfOperation ( ) {
180- this = Crypto:: TModeOfOperationAlgorithm ( instance ) and
252+ this = Crypto:: TBlockCipherModeOfOperationAlgorithm ( instance ) and
181253 // TODO: this currently only holds for explicitly defined modes in a string literal.
182254 // Cases with defaults, e.g., "AES", are not yet modelled.
183255 // For these cases, in a CBOM, the AES node would have an unknown edge to its mode child.
@@ -190,7 +262,7 @@ module JCAModel {
190262 // TODO: handle defaults
191263 override string getRawAlgorithmName ( ) { result = instance .getOrigin ( ) .getMode ( ) }
192264
193- private predicate modeToNameMappingKnown ( Crypto:: TModeOperationType type , string name ) {
265+ private predicate modeToNameMappingKnown ( Crypto:: TBlockCipherModeOperationType type , string name ) {
194266 type instanceof Crypto:: ECB and name = "ECB"
195267 or
196268 type instanceof Crypto:: CBC and name = "CBC"
@@ -208,7 +280,7 @@ module JCAModel {
208280 type instanceof Crypto:: OCB and name = "OCB"
209281 }
210282
211- override Crypto:: TModeOperationType getModeType ( ) {
283+ override Crypto:: TBlockCipherModeOperationType getModeType ( ) {
212284 if this .modeToNameMappingKnown ( _, instance .getOrigin ( ) .getMode ( ) )
213285 then this .modeToNameMappingKnown ( result , instance .getOrigin ( ) .getMode ( ) )
214286 else result instanceof Crypto:: OtherMode
@@ -339,14 +411,51 @@ module JCAModel {
339411 override Expr getInput ( ) { result = this .( ClassInstanceExpr ) .getArgument ( 1 ) }
340412 }
341413
414+ private module JavaxCipherModeAccessToInitConfig implements DataFlow:: ConfigSig {
415+ predicate isSource ( DataFlow:: Node src ) {
416+ src .asExpr ( ) instanceof JavaxCryptoCipherOperationModeAccess
417+ }
418+
419+ predicate isSink ( DataFlow:: Node sink ) {
420+ exists ( CipherInitCall c | c .getModeArg ( ) = sink .asExpr ( ) )
421+ }
422+ }
423+
424+ module JavaxCipherModeAccessToInitFlow = DataFlow:: Global< JavaxCipherModeAccessToInitConfig > ;
425+
342426 class CipherInitCall extends MethodCall {
343427 CipherInitCall ( ) { this .getCallee ( ) .hasQualifiedName ( "javax.crypto" , "Cipher" , "init" ) }
344428
345- // TODO: this doesn't account for tracing the mode to this arg if expending this arg to have
346- // the actual mode directly
429+ /**
430+ * Returns the mode argument to the `init` method
431+ * that is used to determine the cipher operation mode.
432+ * Note this is the raw expr and not necessarily a direct access
433+ * of a mode. Use `getModeOrigin()` to get the field access origin
434+ * flowing to this argument, if one exists (is known).
435+ */
347436 Expr getModeArg ( ) { result = this .getArgument ( 0 ) }
348437
349- // TODO: need a getModeOrigin
438+ JavaxCryptoCipherOperationModeAccess getModeOrigin ( ) {
439+ exists ( DataFlow:: Node src , DataFlow:: Node sink |
440+ JavaxCipherModeAccessToInitFlow:: flow ( src , sink ) and
441+ src .asExpr ( ) = result and
442+ this .getModeArg ( ) = sink .asExpr ( )
443+ )
444+ }
445+
446+ Crypto:: CipherOperationMode getCipherOperationModeType ( ) {
447+ if not exists ( this .getModeOrigin ( ) )
448+ then result instanceof Crypto:: UnknownCipherOperationMode
449+ else
450+ if this .getModeOrigin ( ) .getField ( ) .getName ( ) in [ "ENCRYPT_MODE" , "WRAP_MODE" ]
451+ then result instanceof Crypto:: EncryptionMode
452+ else
453+ if this .getModeOrigin ( ) .getField ( ) .getName ( ) in [ "DECRYPT_MODE" , "UNWRAP_MODE" ]
454+ then result instanceof Crypto:: DecryptionMode
455+ else
456+ // TODO/Question: distinguish between unknown vs unspecified? (the field access is not recognized, vs no field access is found)
457+ result instanceof Crypto:: UnknownCipherOperationMode
458+ }
350459
351460 Expr getKey ( ) {
352461 result = this .getArgument ( 1 ) and this .getMethod ( ) .getParameterType ( 1 ) .hasName ( "Key" )
0 commit comments