@@ -4,39 +4,39 @@ import semmle.code.java.dataflow.DataFlow
44module JCAModel {
55 import Language
66
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
108 bindingset [ algo]
119 predicate cipher_names ( string algo ) {
12- // "Standard names are not case-sensitive."
1310 algo .toUpperCase ( )
1411 .matches ( [
1512 "AES" , "AESWrap" , "AESWrapPad" , "ARCFOUR" , "Blowfish" , "ChaCha20" , "ChaCha20-Poly1305" ,
1613 "DES" , "DESede" , "DESedeWrap" , "ECIES" , "PBEWith%" , "RC2" , "RC4" , "RC5" , "RSA"
1714 ] .toUpperCase ( ) )
1815 }
1916
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]
2119 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 ( ) )
2725 }
2826
29- //todo same as above, OAEPWith has asuffix type
27+ // TODO: Verify that the OAEPWith% case works correctly
28+ bindingset [ padding]
3029 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 ( ) )
3636 }
3737
3838 /**
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
4040 */
4141 class CipherStringLiteral extends StringLiteral {
4242 CipherStringLiteral ( ) { cipher_names ( this .getValue ( ) .splitAt ( "/" ) ) }
@@ -56,6 +56,9 @@ module JCAModel {
5656 Expr getAlgorithmArg ( ) { result = this .getArgument ( 0 ) }
5757 }
5858
59+ /**
60+ * Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument.
61+ */
5962 private module AlgorithmStringToFetchConfig implements DataFlow:: ConfigSig {
6063 predicate isSource ( DataFlow:: Node src ) { src .asExpr ( ) instanceof CipherStringLiteral }
6164
@@ -66,70 +69,77 @@ module JCAModel {
6669
6770 module AlgorithmStringToFetchFlow = DataFlow:: Global< AlgorithmStringToFetchConfig > ;
6871
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+ {
7080 CipherGetInstanceAlgorithmArg ( ) {
7181 exists ( CipherGetInstanceCall call | this = call .getArgument ( 0 ) )
7282 }
7383
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 * ( ) ) )
7690 }
7791 }
7892
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 ;
81106
82- ModeStringLiteral ( ) {
107+ ModeOfOperation ( ) {
83108 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 ( ) )
86113 }
87114
88115 override Location getLocation ( ) { result = instance .getLocation ( ) }
89116
90- override string getRawAlgorithmName ( ) { result = instance .getMode ( ) }
117+ override string getRawAlgorithmName ( ) { result = instance .getOrigin ( ) . getValue ( ) }
91118
92119 predicate modeToNameMapping ( Crypto:: TModeOperationType type , string name ) {
93120 super .modeToNameMapping ( type , name )
94121 }
95122
96123 override Crypto:: TModeOperationType getModeType ( ) {
97- this .modeToNameMapping ( result , instance .getMode ( ) .toUpperCase ( ) )
124+ this .modeToNameMapping ( result , instance .getOrigin ( ) .getMode ( ) )
98125 }
99126
100127 CipherStringLiteral getInstance ( ) { result = instance }
101128 }
102129
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 {
121131 CipherStringLiteral origin ;
122132 CipherGetInstanceAlgorithmArg instance ;
123133
124- CipherAlgorithm ( ) {
125- this = Crypto:: TSymmetricAlgorithm ( instance ) and
134+ EncryptionAlgorithm ( ) {
135+ this = Crypto:: TEncryptionAlgorithm ( instance ) and
126136 instance .getOrigin ( ) = origin
127137 }
128138
129139 override Location getLocation ( ) { result = instance .getLocation ( ) }
130140
131- override Crypto:: ModeOfOperation getModeOfOperation ( ) {
132- result .( ModeStringLiteral ) .getInstance ( ) = origin
141+ override Crypto:: ModeOfOperationAlgorithm getModeOfOperation ( ) {
142+ result .( ModeOfOperation ) .getInstance ( ) = origin
133143 }
134144
135145 override Crypto:: LocatableElement getOrigin ( string name ) {
@@ -138,23 +148,25 @@ module JCAModel {
138148
139149 override string getRawAlgorithmName ( ) { result = origin .getValue ( ) }
140150
141- override Crypto:: TSymmetricCipherType getCipherFamily ( ) {
151+ override Crypto:: TCipherType getCipherFamily ( ) {
142152 this .cipherNameMapping ( result , origin .getAlgorithmName ( ) )
143153 }
144154
145155 override string getKeySize ( Location location ) { none ( ) }
146156
147157 bindingset [ name]
148- private predicate cipherNameMappingKnown ( Crypto:: TSymmetricCipherType type , string name ) {
158+ private predicate cipherNameMappingKnown ( Crypto:: TCipherType type , string name ) {
149159 name = "AES" and
150160 type instanceof Crypto:: AES
151161 or
152162 name = "RC4" and
153163 type instanceof Crypto:: RC4
164+ // or
165+ // TODO
154166 }
155167
156168 bindingset [ name]
157- predicate cipherNameMapping ( Crypto:: TSymmetricCipherType type , string name ) {
169+ predicate cipherNameMapping ( Crypto:: TCipherType type , string name ) {
158170 this .cipherNameMappingKnown ( type , name )
159171 or
160172 not this .cipherNameMappingKnown ( _, name ) and
0 commit comments