Skip to content

Commit b777a22

Browse files
committed
Expand model and specialize newtype relations
1 parent 874e3b5 commit b777a22

File tree

4 files changed

+203
-112
lines changed

4 files changed

+203
-112
lines changed

cpp/ql/lib/experimental/Quantum/OpenSSL.qll

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,6 @@ import semmle.code.cpp.dataflow.new.DataFlow
44
module OpenSSLModel {
55
import Language
66

7-
class FunctionCallOrMacroAccess extends Element {
8-
FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess }
9-
10-
string getTargetName() {
11-
result = this.(FunctionCall).getTarget().getName()
12-
or
13-
result = this.(MacroAccess).getMacroName()
14-
}
15-
}
16-
177
/**
188
* Hash function references in OpenSSL.
199
*/
@@ -42,13 +32,27 @@ module OpenSSLModel {
4232
hash_ref_type_mapping_known(name, algo)
4333
}
4434

45-
class HashAlgorithmRef extends Crypto::HashAlgorithm {
46-
FunctionCallOrMacroAccess instance;
35+
class FunctionCallOrMacroAccess extends Element {
36+
FunctionCallOrMacroAccess() { this instanceof FunctionCall or this instanceof MacroAccess }
4737

48-
HashAlgorithmRef() {
49-
this = Crypto::THashAlgorithm(instance) and
50-
hash_ref_type_mapping(instance, _, _)
38+
string getTargetName() {
39+
result = this.(FunctionCall).getTarget().getName()
40+
or
41+
result = this.(MacroAccess).getMacroName()
5142
}
43+
}
44+
45+
class HashAlgorithmCallOrMacro extends Crypto::HashAlgorithmInstance instanceof FunctionCallOrMacroAccess
46+
{
47+
HashAlgorithmCallOrMacro() { hash_ref_type_mapping(this, _, _) }
48+
49+
string getTargetName() { result = this.(FunctionCallOrMacroAccess).getTargetName() }
50+
}
51+
52+
class HashAlgorithm extends Crypto::HashAlgorithm {
53+
HashAlgorithmCallOrMacro instance;
54+
55+
HashAlgorithm() { this = Crypto::THashAlgorithm(instance) }
5256

5357
override string getSHA2OrSHA3DigestSize(Location location) {
5458
(
@@ -81,9 +85,9 @@ module OpenSSLModel {
8185

8286
predicate isSink(DataFlow::Node sink) {
8387
exists(EVP_KDF_derive kdo |
84-
sink.asExpr() = kdo.getAlgorithmArg()
88+
sink.asExpr() = kdo.getCall().getAlgorithmArg()
8589
or
86-
sink.asExpr() = kdo.getContextArg() // via `EVP_KDF_CTX_set_params`
90+
sink.asExpr() = kdo.getCall().getContextArg() // via `EVP_KDF_CTX_set_params`
8791
)
8892
}
8993

@@ -101,21 +105,23 @@ module OpenSSLModel {
101105
/**
102106
* Key derivation operation (e.g., `EVP_KDF_derive`)
103107
*/
104-
abstract class KeyDerivationOperation extends Crypto::KeyDerivationOperation { }
108+
class EVP_KDF_derive_FunctionCall extends Crypto::KeyDerivationOperationInstance instanceof FunctionCall
109+
{
110+
EVP_KDF_derive_FunctionCall() { this.getTarget().getName() = "EVP_KDF_derive" }
105111

106-
class EVP_KDF_derive extends KeyDerivationOperation {
107-
FunctionCall instance;
112+
Expr getAlgorithmArg() { result = super.getArgument(3) }
108113

109-
EVP_KDF_derive() {
110-
this = Crypto::TKeyDerivationOperation(instance) and
111-
instance.getTarget().getName() = "EVP_KDF_derive"
112-
}
114+
Expr getContextArg() { result = super.getArgument(0) }
115+
}
113116

114-
override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) }
117+
class EVP_KDF_derive extends Crypto::KeyDerivationOperation {
118+
EVP_KDF_derive_FunctionCall instance;
119+
120+
EVP_KDF_derive() { this = Crypto::TKeyDerivationOperation(instance) }
115121

116-
Expr getAlgorithmArg() { result = instance.getArgument(3) }
122+
override Crypto::Algorithm getAlgorithm() { algorithm_to_EVP_KDF_derive(result, this) }
117123

118-
Expr getContextArg() { result = instance.getArgument(0) }
124+
EVP_KDF_derive_FunctionCall getCall() { result = instance }
119125
}
120126

121127
/**
@@ -134,7 +140,7 @@ module OpenSSLModel {
134140
Expr getAlgorithmArg() { result = this.getArgument(1) }
135141
}
136142

137-
class EVP_KDF_fetch_AlgorithmArg extends Expr {
143+
class EVP_KDF_fetch_AlgorithmArg extends Crypto::KeyDerivationAlgorithmInstance instanceof Expr {
138144
EVP_KDF_fetch_AlgorithmArg() { exists(EVP_KDF_fetch_Call call | this = call.getAlgorithmArg()) }
139145
}
140146

java/ql/lib/experimental/Quantum/JCA.qll

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ import semmle.code.java.dataflow.DataFlow
44
module 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

java/ql/src/experimental/Quantum/Test.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
import experimental.Quantum.Language
66

7-
from Crypto::SymmetricAlgorithm a, Crypto::ModeOfOperation mode
7+
from Crypto::EncryptionAlgorithm a, Crypto::ModeOfOperationAlgorithm mode
88
where a.getModeOfOperation() = mode
99
select a, a.getAlgorithmName(), a.getRawAlgorithmName(), mode, mode.getAlgorithmName()

0 commit comments

Comments
 (0)