@@ -4,39 +4,39 @@ import semmle.code.java.dataflow.DataFlow
4
4
module JCAModel {
5
5
import Language
6
6
7
- abstract class EncryptionOperation extends Crypto:: EncryptionOperation { }
8
-
9
- //TODO PBEWith can have suffixes. how to do? enumerate? or match a pattern?
7
+ // TODO: Verify that the PBEWith% case works correctly
10
8
bindingset [ algo]
11
9
predicate cipher_names ( string algo ) {
12
- // "Standard names are not case-sensitive."
13
10
algo .toUpperCase ( )
14
11
.matches ( [
15
12
"AES" , "AESWrap" , "AESWrapPad" , "ARCFOUR" , "Blowfish" , "ChaCha20" , "ChaCha20-Poly1305" ,
16
13
"DES" , "DESede" , "DESedeWrap" , "ECIES" , "PBEWith%" , "RC2" , "RC4" , "RC5" , "RSA"
17
14
] .toUpperCase ( ) )
18
15
}
19
16
20
- //TODO solve the fact that x is an int of various values. same as above... enumerate?
17
+ // TODO: Verify that the CFB% case works correctly
18
+ bindingset [ mode]
21
19
predicate cipher_modes ( string mode ) {
22
- mode =
23
- [
24
- "NONE" , "CBC" , "CCM" , "CFB" , "CFBx " , "CTR" , "CTS" , "ECB" , "GCM" , "KW" , "KWP" , "OFB" , "OFBx ",
25
- "PCBC"
26
- ]
20
+ mode . toUpperCase ( )
21
+ . matches ( [
22
+ "NONE" , "CBC" , "CCM" , "CFB" , "CFB% " , "CTR" , "CTS" , "ECB" , "GCM" , "KW" , "KWP" , "OFB" ,
23
+ "OFB%" , "PCBC"
24
+ ] . toUpperCase ( ) )
27
25
}
28
26
29
- //todo same as above, OAEPWith has asuffix type
27
+ // TODO: Verify that the OAEPWith% case works correctly
28
+ bindingset [ padding]
30
29
predicate cipher_padding ( string padding ) {
31
- padding =
32
- [
33
- "NoPadding" , "ISO10126Padding" , "OAEPPadding" , "OAEPWith" , "PKCS1Padding" , "PKCS5Padding" ,
34
- "SSL3Padding"
35
- ]
30
+ padding
31
+ .toUpperCase ( )
32
+ .matches ( [
33
+ "NoPadding" , "ISO10126Padding" , "OAEPPadding" , "OAEPWith%" , "PKCS1Padding" ,
34
+ "PKCS5Padding" , "SSL3Padding"
35
+ ] .toUpperCase ( ) )
36
36
}
37
37
38
38
/**
39
- * this may be specified either in the ALG/MODE/PADDING or just ALG format
39
+ * A `StringLiteral` in the `" ALG/MODE/PADDING"` or `" ALG"` format
40
40
*/
41
41
class CipherStringLiteral extends StringLiteral {
42
42
CipherStringLiteral ( ) { cipher_names ( this .getValue ( ) .splitAt ( "/" ) ) }
@@ -56,6 +56,9 @@ module JCAModel {
56
56
Expr getAlgorithmArg ( ) { result = this .getArgument ( 0 ) }
57
57
}
58
58
59
+ /**
60
+ * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument.
61
+ */
59
62
private module AlgorithmStringToFetchConfig implements DataFlow:: ConfigSig {
60
63
predicate isSource ( DataFlow:: Node src ) { src .asExpr ( ) instanceof CipherStringLiteral }
61
64
@@ -66,70 +69,77 @@ module JCAModel {
66
69
67
70
module AlgorithmStringToFetchFlow = DataFlow:: Global< AlgorithmStringToFetchConfig > ;
68
71
69
- class CipherGetInstanceAlgorithmArg extends Expr {
72
+ /**
73
+ * The cipher algorithm argument to a `CipherGetInstanceCall`.
74
+ *
75
+ * For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`.
76
+ */
77
+ class CipherGetInstanceAlgorithmArg extends Crypto:: EncryptionAlgorithmInstance ,
78
+ Crypto:: ModeOfOperationAlgorithmInstance instanceof Expr
79
+ {
70
80
CipherGetInstanceAlgorithmArg ( ) {
71
81
exists ( CipherGetInstanceCall call | this = call .getArgument ( 0 ) )
72
82
}
73
83
74
- StringLiteral getOrigin ( ) {
75
- AlgorithmStringToFetchFlow:: flow ( DataFlow:: exprNode ( result ) , DataFlow:: exprNode ( this ) )
84
+ /**
85
+ * Returns the `StringLiteral` from which this argument is derived, if known.
86
+ */
87
+ CipherStringLiteral getOrigin ( ) {
88
+ AlgorithmStringToFetchFlow:: flow ( DataFlow:: exprNode ( result ) ,
89
+ DataFlow:: exprNode ( this .( Expr ) .getAChildExpr * ( ) ) )
76
90
}
77
91
}
78
92
79
- class ModeStringLiteral extends Crypto:: ModeOfOperation {
80
- CipherStringLiteral instance ;
93
+ /**
94
+ * A block cipher mode of operation, where the mode is specified in the ALG or ALG/MODE/PADDING format.
95
+ *
96
+ * This class will only exist when the mode (*and its type*) is determinable.
97
+ * This is because the mode will always be specified alongside the algorithm and never independently.
98
+ * Therefore, we can always assume that a determinable algorithm will have a determinable mode.
99
+ *
100
+ * In the case that only an algorithm is specified, e.g., "AES", the provider provides a default mode.
101
+ *
102
+ * TODO: Model the case of relying on a provider default, but alert on it as a bad practice.
103
+ */
104
+ class ModeOfOperation extends Crypto:: ModeOfOperationAlgorithm {
105
+ CipherGetInstanceAlgorithmArg instance ;
81
106
82
- ModeStringLiteral ( ) {
107
+ ModeOfOperation ( ) {
83
108
this = Crypto:: TModeOfOperationAlgorithm ( instance ) and
84
- exists ( instance .getMode ( ) ) and
85
- instance = any ( CipherGetInstanceAlgorithmArg call ) .getOrigin ( )
109
+ // TODO: this currently only holds for explicitly defined modes in a string literal.
110
+ // Cases with defaults, e.g., "AES", are not yet modelled.
111
+ // For these cases, in a CBOM, the AES node would have an unknown edge to its mode child.
112
+ exists ( instance .getOrigin ( ) .getMode ( ) )
86
113
}
87
114
88
115
override Location getLocation ( ) { result = instance .getLocation ( ) }
89
116
90
- override string getRawAlgorithmName ( ) { result = instance .getMode ( ) }
117
+ override string getRawAlgorithmName ( ) { result = instance .getOrigin ( ) . getValue ( ) }
91
118
92
119
predicate modeToNameMapping ( Crypto:: TModeOperationType type , string name ) {
93
120
super .modeToNameMapping ( type , name )
94
121
}
95
122
96
123
override Crypto:: TModeOperationType getModeType ( ) {
97
- this .modeToNameMapping ( result , instance .getMode ( ) .toUpperCase ( ) )
124
+ this .modeToNameMapping ( result , instance .getOrigin ( ) .getMode ( ) )
98
125
}
99
126
100
127
CipherStringLiteral getInstance ( ) { result = instance }
101
128
}
102
129
103
- //todo refactor
104
- // class CipherAlgorithmPaddingStringLiteral extends CipherAlgorithmPadding instanceof StringLiteral {
105
- // CipherAlgorithmPaddingStringLiteral() {
106
- // cipher_padding(this.(StringLiteral).getValue().splitAt("/"))
107
- // }
108
- // override string toString() { result = this.(StringLiteral).toString() }
109
- // override string getValue() {
110
- // result = this.(StringLiteral).getValue().regexpCapture(".*/.*/(.*)", 1)
111
- // }
112
- // }
113
- /**
114
- * A class to represent when AES is used
115
- * AND currently it has literal mode and padding provided
116
- *
117
- * this currently does not capture the use without a literal
118
- * though should be extended to
119
- */
120
- class CipherAlgorithm extends Crypto:: SymmetricAlgorithm {
130
+ class EncryptionAlgorithm extends Crypto:: EncryptionAlgorithm {
121
131
CipherStringLiteral origin ;
122
132
CipherGetInstanceAlgorithmArg instance ;
123
133
124
- CipherAlgorithm ( ) {
125
- this = Crypto:: TSymmetricAlgorithm ( instance ) and
134
+ EncryptionAlgorithm ( ) {
135
+ this = Crypto:: TEncryptionAlgorithm ( instance ) and
126
136
instance .getOrigin ( ) = origin
127
137
}
128
138
129
139
override Location getLocation ( ) { result = instance .getLocation ( ) }
130
140
131
- override Crypto:: ModeOfOperation getModeOfOperation ( ) {
132
- result .( ModeStringLiteral ) .getInstance ( ) = origin
141
+ override Crypto:: ModeOfOperationAlgorithm getModeOfOperation ( ) {
142
+ result .( ModeOfOperation ) .getInstance ( ) = origin
133
143
}
134
144
135
145
override Crypto:: LocatableElement getOrigin ( string name ) {
@@ -138,23 +148,25 @@ module JCAModel {
138
148
139
149
override string getRawAlgorithmName ( ) { result = origin .getValue ( ) }
140
150
141
- override Crypto:: TSymmetricCipherType getCipherFamily ( ) {
151
+ override Crypto:: TCipherType getCipherFamily ( ) {
142
152
this .cipherNameMapping ( result , origin .getAlgorithmName ( ) )
143
153
}
144
154
145
155
override string getKeySize ( Location location ) { none ( ) }
146
156
147
157
bindingset [ name]
148
- private predicate cipherNameMappingKnown ( Crypto:: TSymmetricCipherType type , string name ) {
158
+ private predicate cipherNameMappingKnown ( Crypto:: TCipherType type , string name ) {
149
159
name = "AES" and
150
160
type instanceof Crypto:: AES
151
161
or
152
162
name = "RC4" and
153
163
type instanceof Crypto:: RC4
164
+ // or
165
+ // TODO
154
166
}
155
167
156
168
bindingset [ name]
157
- predicate cipherNameMapping ( Crypto:: TSymmetricCipherType type , string name ) {
169
+ predicate cipherNameMapping ( Crypto:: TCipherType type , string name ) {
158
170
this .cipherNameMappingKnown ( type , name )
159
171
or
160
172
not this .cipherNameMappingKnown ( _, name ) and
0 commit comments