Skip to content

Commit 4f2045b

Browse files
committed
Crypto: CtxFlow now uses an interface for additional steps. Add CTX step to handle paramgen. Remove redundant test. Overhaul of EVP update/initializer/final mechanics. Misc. updates for new API and refactoring EVPKeyGenOperation. Clean up of keygen_operaitons.ql.
1 parent 98aae6a commit 4f2045b

File tree

9 files changed

+265
-142
lines changed

9 files changed

+265
-142
lines changed

cpp/ql/lib/experimental/quantum/Language.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ module CryptoInput implements InputSig<Language::Location> {
1313
LocatableElement dfn_to_element(DataFlow::Node node) {
1414
result = node.asExpr() or
1515
result = node.asParameter() or
16-
result = node.asVariable()
16+
result = node.asVariable() or
17+
result = node.asDefiningArgument()
18+
// TODO: do we need asIndirectExpr()?
1719
}
1820

1921
string locationToFileBaseNameAndLineNumberString(Location location) {

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmValueConsumers/PKeyAlgorithmValueConsumer.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class EVPPKeyAlgorithmConsumer extends PKeyValueConsumer {
2323
or
2424
this.(Call).getTarget().getName() in [
2525
"EVP_PKEY_CTX_new_from_name", "EVP_PKEY_new_raw_private_key_ex",
26-
"EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"
26+
"EVP_PKEY_new_raw_public_key_ex", "EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_ctrl_uint64",
27+
"EVP_PKEY_CTX_ctrl_str", "EVP_PKEY_CTX_set_group_name"
2728
] and
2829
valueArgNode.asExpr() = this.(Call).getArgument(1)
2930
or

cpp/ql/lib/experimental/quantum/OpenSSL/CtxFlow.qll

Lines changed: 86 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import semmle.code.cpp.dataflow.new.DataFlow
2828
* - EVP_MD_CTX
2929
* - EVP_PKEY_CTX
3030
*/
31-
private class CtxType extends Type {
31+
class CtxType extends Type {
3232
CtxType() {
3333
// It is possible for users to use the underlying type of the CTX variables
3434
// these have a name matching 'evp_%ctx_%st
@@ -47,7 +47,7 @@ private class CtxType extends Type {
4747
/**
4848
* A pointer to a CtxType
4949
*/
50-
private class CtxPointerExpr extends Expr {
50+
class CtxPointerExpr extends Expr {
5151
CtxPointerExpr() {
5252
this.getType() instanceof CtxType and
5353
this.getType() instanceof PointerType
@@ -57,7 +57,7 @@ private class CtxPointerExpr extends Expr {
5757
/**
5858
* A call argument of type CtxPointerExpr.
5959
*/
60-
private class CtxPointerArgument extends CtxPointerExpr {
60+
class CtxPointerArgument extends CtxPointerExpr {
6161
CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) }
6262

6363
Call getCall() { result.getAnArgument() = this }
@@ -83,32 +83,103 @@ private class CtxClearCall extends Call {
8383
}
8484
}
8585

86+
abstract private class CtxPassThroughCall extends Call {
87+
abstract DataFlow::Node getNode1();
88+
89+
abstract DataFlow::Node getNode2();
90+
}
91+
8692
/**
8793
* A call whose target contains 'copy' and has an argument of type
8894
* CtxPointerArgument.
8995
*/
90-
private class CtxCopyOutArgCall extends Call {
96+
private class CtxCopyOutArgCall extends CtxPassThroughCall {
97+
DataFlow::Node n1;
98+
DataFlow::Node n2;
99+
91100
CtxCopyOutArgCall() {
92101
this.getTarget().getName().toLowerCase().matches("%copy%") and
93-
this.getAnArgument() instanceof CtxPointerArgument
102+
n1.asExpr() = this.getAnArgument() and
103+
n1.getType() instanceof CtxType and
104+
n2.asDefiningArgument() = this.getAnArgument() and
105+
n2.getType() instanceof CtxType and
106+
n1.asDefiningArgument() != n2.asExpr()
94107
}
108+
109+
override DataFlow::Node getNode1() { result = n1 }
110+
111+
override DataFlow::Node getNode2() { result = n2 }
95112
}
96113

97114
/**
98115
* A call whose target contains 'dup' and has an argument of type
99116
* CtxPointerArgument.
100117
*/
101-
private class CtxCopyReturnCall extends Call, CtxPointerExpr {
118+
private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr {
119+
DataFlow::Node n1;
120+
102121
CtxCopyReturnCall() {
103122
this.getTarget().getName().toLowerCase().matches("%dup%") and
104-
this.getAnArgument() instanceof CtxPointerArgument
123+
n1.asExpr() = this.getAnArgument() and
124+
n1.getType() instanceof CtxType
125+
}
126+
127+
override DataFlow::Node getNode1() { result = n1 }
128+
129+
override DataFlow::Node getNode2() { result.asExpr() = this }
130+
}
131+
132+
/**
133+
* A call to `EVP_PKEY_paramgen` acts as a kind of pass through.
134+
* It's output pkey is eventually used in a new operation generating
135+
* a fresh context pointer (e.g., `EVP_PKEY_CTX_new`).
136+
* It is easier to model this as a pass through
137+
* than to model the flow from the paramgen to the new key generation.
138+
*/
139+
private class CtxParamGenCall extends CtxPassThroughCall {
140+
DataFlow::Node n1;
141+
DataFlow::Node n2;
142+
143+
CtxParamGenCall() {
144+
this.getTarget().getName() = "EVP_PKEY_paramgen" and
145+
n1.asExpr() = this.getArgument(0) and
146+
(
147+
n2.asExpr() = this.getArgument(1)
148+
or
149+
n2.asDefiningArgument() = this.getArgument(1)
150+
)
105151
}
152+
153+
override DataFlow::Node getNode1() { result = n1 }
154+
155+
override DataFlow::Node getNode2() { result = n2 }
156+
}
157+
158+
/**
159+
* If the current node gets is an argument to a function
160+
* that returns a pointer type, immediately flow through.
161+
* NOTE: this passthrough is required if we allow
162+
* intermediate steps to go into variables that are not a CTX type.
163+
* See for example `CtxParamGenCall`.
164+
*/
165+
private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr {
166+
DataFlow::Node n1;
167+
DataFlow::Node n2;
168+
169+
CallArgToCtxRet() {
170+
this.getAnArgument() = n1.asExpr() and
171+
n2.asExpr() = this
172+
}
173+
174+
override DataFlow::Node getNode1() { result = n1 }
175+
176+
override DataFlow::Node getNode2() { result = n2 }
106177
}
107178

108179
/**
109180
* A source Ctx of interest is any argument or return of type CtxPointerExpr.
110181
*/
111-
private class CtxPointerSource extends CtxPointerExpr {
182+
class CtxPointerSource extends CtxPointerExpr {
112183
CtxPointerSource() {
113184
this instanceof CtxPointerReturn or
114185
this instanceof CtxPointerArgument
@@ -122,43 +193,31 @@ private class CtxPointerSource extends CtxPointerExpr {
122193
}
123194

124195
/**
125-
* Flow from any CtxPointerSource to any CtxPointerArgument.
196+
* Flow from any CtxPointerSource to other CtxPointerSource.
126197
*/
127-
module OpenSSLCtxSourceToArgumentFlowConfig implements DataFlow::ConfigSig {
198+
module OpenSSLCtxSourceToSourceFlowConfig implements DataFlow::ConfigSig {
128199
predicate isSource(DataFlow::Node source) { exists(CtxPointerSource s | s.asNode() = source) }
129200

130-
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof CtxPointerArgument }
201+
predicate isSink(DataFlow::Node sink) { exists(CtxPointerSource s | s.asNode() = sink) }
131202

132203
predicate isBarrier(DataFlow::Node node) {
133204
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
134205
}
135206

136207
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
137-
exists(CtxCopyOutArgCall c |
138-
c.getAnArgument() = node1.asExpr() and
139-
c.getAnArgument() = node2.asExpr() and
140-
node1.asExpr() != node2.asExpr() and
141-
node2.asExpr().getType() instanceof CtxType
142-
)
143-
or
144-
exists(CtxCopyReturnCall c |
145-
c.getAnArgument() = node1.asExpr() and
146-
c = node2.asExpr() and
147-
node1.asExpr() != node2.asExpr() and
148-
node2.asExpr().getType() instanceof CtxType
149-
)
208+
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
150209
}
151210
}
152211

153-
module OpenSSLCtxSourceToArgumentFlow = DataFlow::Global<OpenSSLCtxSourceToArgumentFlowConfig>;
212+
module OpenSSLCtxSourceToArgumentFlow = DataFlow::Global<OpenSSLCtxSourceToSourceFlowConfig>;
154213

155214
/**
156215
* Holds if there is a context flow from the source to the sink.
157216
*/
158-
predicate ctxArgOrRetFlowsToCtxArg(CtxPointerSource source, CtxPointerArgument sink) {
217+
predicate ctxSrcToSrcFlow(CtxPointerSource source, CtxPointerSource sink) {
159218
exists(DataFlow::Node a, DataFlow::Node b |
160219
OpenSSLCtxSourceToArgumentFlow::flow(a, b) and
161220
a = source.asNode() and
162-
b.asExpr() = sink
221+
b = sink.asNode()
163222
)
164223
}

cpp/ql/lib/experimental/quantum/OpenSSL/Operations/EVPCipherOperation.qll

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
private import experimental.quantum.Language
2-
private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow
2+
private import experimental.quantum.OpenSSL.CtxFlow
33
private import OpenSSLOperationBase
44
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
55

@@ -31,7 +31,11 @@ Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
3131
}
3232

3333
// TODO: need to add key consumer
34-
abstract class EVP_Cipher_Initializer extends EVPInitialize {
34+
abstract class EVP_Cipher_Initializer extends EvpKeyOperationSubtypeInitializer,
35+
EvpAlgorithmInitializer, EvpKeyInitializer, EvpIVInitializer
36+
{
37+
override CtxPointerSource getContextArg() { result = this.(Call).getArgument(0) }
38+
3539
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
3640

3741
abstract Expr getOperationSubtypeArg();
@@ -94,13 +98,15 @@ class EVP_CipherInit_SKEY_Call extends EVP_EX2_Initializer {
9498
override Expr getOperationSubtypeArg() { result = this.(Call).getArgument(5) }
9599
}
96100

97-
class EVP_Cipher_Update_Call extends EVPUpdate {
101+
class EVP_Cipher_Update_Call extends EvpUpdate {
98102
EVP_Cipher_Update_Call() {
99103
this.(Call).getTarget().getName() in [
100104
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
101105
]
102106
}
103107

108+
override CtxPointerSource getContextArg() { result = this.(Call).getArgument(0) }
109+
104110
override Expr getInputArg() { result = this.(Call).getArgument(3) }
105111

106112
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
@@ -110,7 +116,7 @@ class EVP_Cipher_Update_Call extends EVPUpdate {
110116
* see: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
111117
* Base configuration for all EVP cipher operations.
112118
*/
113-
abstract class EVP_Cipher_Operation extends EVPOperation, Crypto::KeyOperationInstance {
119+
abstract class EVP_Cipher_Operation extends EvpOperation, Crypto::KeyOperationInstance {
114120
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
115121

116122
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
@@ -120,34 +126,38 @@ abstract class EVP_Cipher_Operation extends EVPOperation, Crypto::KeyOperationIn
120126
result instanceof Crypto::TDecryptMode and
121127
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
122128
or
123-
result = this.getInitCall().getKeyOperationSubtype() and
129+
result = this.getInitCall().(EvpKeyOperationSubtypeInitializer).getKeyOperationSubtype() and
124130
this.(Call).getTarget().getName().toLowerCase().matches("%cipher%")
125131
}
126132

127133
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
128-
this.getInitCall().getIVArg() = result.asExpr()
134+
this.getInitCall().(EvpIVInitializer).getIVArg() = result.asExpr()
129135
}
130136

131137
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
132-
this.getInitCall().getKeyArg() = result.asExpr()
138+
this.getInitCall().(EvpKeyInitializer).getKeyArg() = result.asExpr()
133139
// todo: or track to the EVP_PKEY_CTX_new
134140
}
135141

136142
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
137-
result = EVPOperation.super.getOutputArtifact()
143+
result = EvpOperation.super.getOutputArtifact()
138144
}
139145

140146
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
141-
result = EVPOperation.super.getInputConsumer()
147+
result = EvpOperation.super.getInputConsumer()
142148
}
143149
}
144150

145-
class EVP_Cipher_Call extends EVPOperation, EVP_Cipher_Operation {
151+
class EVP_Cipher_Call extends EvpOperation, EVP_Cipher_Operation {
146152
EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }
147153

148154
override Expr getInputArg() { result = this.(Call).getArgument(2) }
149155

150-
override Expr getAlgorithmArg() { result = this.getInitCall().getAlgorithmArg() }
156+
override Expr getAlgorithmArg() {
157+
result = this.getInitCall().(EvpAlgorithmInitializer).getAlgorithmArg()
158+
}
159+
160+
override CtxPointerSource getContextArg() { result = this.(Call).getArgument(0) }
151161
}
152162

153163
class EVP_Cipher_Final_Call extends EVPFinal, EVP_Cipher_Operation {
@@ -167,5 +177,9 @@ class EVP_Cipher_Final_Call extends EVPFinal, EVP_Cipher_Operation {
167177
result = EVP_Cipher_Operation.super.getOutputArg()
168178
}
169179

170-
override Expr getAlgorithmArg() { result = this.getInitCall().getAlgorithmArg() }
180+
override Expr getAlgorithmArg() {
181+
result = this.getInitCall().(EvpAlgorithmInitializer).getAlgorithmArg()
182+
}
183+
184+
override CtxPointerSource getContextArg() { result = this.(Call).getArgument(0) }
171185
}

0 commit comments

Comments
 (0)