Skip to content

Commit 6d1b1d1

Browse files
committed
refactor EVP common classes
add initial work for openssl signatures add basic C test files for ciphers and signatures more signature classes, comments for evp base classes more signature tests fix super calls for input consumers fix getOutputArtifact for tests formatting delete redundant test files move algorithm methods to OpenSSLOperation refactor ECKeyGenOperation for new EVP classes formatting fix getOutputArtifact fix cipher and digest operation test results mv openssl signature to another PR
1 parent 8fe2699 commit 6d1b1d1

File tree

9 files changed

+209
-205
lines changed

9 files changed

+209
-205
lines changed

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

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,15 @@ private import OpenSSLOperationBase
44
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
55
private import semmle.code.cpp.dataflow.new.DataFlow
66

7-
private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig {
8-
predicate isSource(DataFlow::Node source) {
9-
exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source)
10-
}
11-
12-
predicate isSink(DataFlow::Node sink) {
13-
exists(ECKeyGenOperation c | c.getAlgorithmArg() = sink.asExpr())
14-
}
15-
}
16-
17-
private module AlgGetterToAlgConsumerFlow = DataFlow::Global<AlgGetterToAlgConsumerConfig>;
18-
197
class ECKeyGenOperation extends OpenSSLOperation, Crypto::KeyGenerationOperationInstance {
208
ECKeyGenOperation() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" }
219

22-
override Expr getOutputArg() {
23-
result = this.(Call) // return value of call
24-
}
25-
26-
Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }
27-
28-
override Expr getInputArg() {
29-
// there is no 'input', in the sense that no data is being manipulated by the operation.
30-
// There is an input of an algorithm, but that is not the intention of the operation input arg.
31-
none()
32-
}
10+
override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }
3311

3412
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
3513

3614
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
37-
result = this.getOutputNode()
38-
}
39-
40-
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
41-
AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(),
42-
DataFlow::exprNode(this.getAlgorithmArg()))
15+
result.asExpr() = this.(Call).getArgument(0)
4316
}
4417

4518
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
private import experimental.quantum.Language
77
private import experimental.quantum.OpenSSL.CtxFlow as CTXFlow
8+
private import OpenSSLOperationBase
89

910
module EncValToInitEncArgConfig implements DataFlow::ConfigSig {
1011
predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] }
@@ -34,19 +35,12 @@ Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
3435
}
3536

3637
// TODO: need to add key consumer
37-
abstract class EVP_Cipher_Initializer extends Call {
38-
Expr getContextArg() { result = this.(Call).getArgument(0) }
38+
abstract class EVP_Cipher_Initializer extends EVPInitialize {
39+
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
3940

40-
Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
41-
42-
abstract Expr getKeyArg();
43-
44-
abstract Expr getIVArg();
45-
46-
// abstract Crypto::CipherOperationSubtype getCipherOperationSubtype();
4741
abstract Expr getOperationSubtypeArg();
4842

49-
Crypto::KeyOperationSubtype getCipherOperationSubtype() {
43+
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
5044
if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
5145
then result instanceof Crypto::TEncryptMode
5246
else

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

Lines changed: 16 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,21 @@ private import EVPCipherInitializer
44
private import OpenSSLOperationBase
55
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
66

7-
private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig {
8-
predicate isSource(DataFlow::Node source) {
9-
exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source)
7+
class EVP_Cipher_Update_Call extends EVPUpdate {
8+
EVP_Cipher_Update_Call() {
9+
this.(Call).getTarget().getName() in [
10+
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
11+
]
1012
}
1113

12-
predicate isSink(DataFlow::Node sink) {
13-
exists(EVP_Cipher_Operation c | c.getAlgorithmArg() = sink.asExpr())
14-
}
14+
override Expr getInputArg() { result = this.(Call).getArgument(3) }
1515
}
1616

17-
private module AlgGetterToAlgConsumerFlow = DataFlow::Global<AlgGetterToAlgConsumerConfig>;
18-
19-
// import experimental.quantum.OpenSSL.AlgorithmValueConsumers.AlgorithmValueConsumers
20-
// import OpenSSLOperation
21-
// class EVPCipherOutput extends CipherOutputArtifact {
22-
// EVPCipherOutput() { exists(EVP_Cipher_Operation op | op.getOutputArg() = this) }
23-
// override DataFlow::Node getOutputNode() { result.asDefiningArgument() = this }
24-
// }
25-
//
2617
/**
2718
* see: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
2819
* Base configuration for all EVP cipher operations.
29-
* NOTE: cannot extend instance of OpenSSLOperation, as we need to override
30-
* elements of OpenSSLOperation (i.e., we are creating an instance)
3120
*/
32-
abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperationInstance {
33-
Expr getContextArg() { result = this.(Call).getArgument(0) }
34-
35-
Expr getAlgorithmArg() { this.getInitCall().getAlgorithmArg() = result }
36-
21+
abstract class EVP_Cipher_Operation extends EVPOperation, Crypto::KeyOperationInstance {
3722
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
3823

3924
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
@@ -43,81 +28,39 @@ abstract class EVP_Cipher_Operation extends OpenSSLOperation, Crypto::KeyOperati
4328
result instanceof Crypto::TDecryptMode and
4429
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
4530
or
46-
result = this.getInitCall().getCipherOperationSubtype() and
31+
result = this.getInitCall().getKeyOperationSubtype() and
4732
this.(Call).getTarget().getName().toLowerCase().matches("%cipher%")
4833
}
4934

50-
EVP_Cipher_Initializer getInitCall() {
51-
CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg())
52-
}
53-
5435
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
5536
this.getInitCall().getIVArg() = result.asExpr()
5637
}
5738

58-
override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }
59-
6039
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
6140
this.getInitCall().getKeyArg() = result.asExpr()
41+
// todo: or track to the EVP_PKEY_CTX_new
6242
}
6343

64-
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() }
44+
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
45+
result = EVPOperation.super.getOutputArtifact()
46+
}
6547

66-
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
67-
AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(),
68-
DataFlow::exprNode(this.getInitCall().getAlgorithmArg()))
48+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
49+
result = EVPOperation.super.getInputConsumer()
6950
}
7051
}
7152

72-
class EVP_Cipher_Call extends EVP_Cipher_Operation {
53+
class EVP_Cipher_Call extends EVPOneShot, EVP_Cipher_Operation {
7354
EVP_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }
7455

7556
override Expr getInputArg() { result = this.(Call).getArgument(2) }
7657
}
7758

78-
// NOTE: not modeled as cipher operations, these are intermediate calls
79-
class EVP_Cipher_Update_Call extends Call {
80-
EVP_Cipher_Update_Call() {
81-
this.(Call).getTarget().getName() in [
82-
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
83-
]
84-
}
85-
86-
Expr getInputArg() { result = this.(Call).getArgument(3) }
87-
88-
DataFlow::Node getInputNode() { result.asExpr() = this.getInputArg() }
89-
90-
Expr getContextArg() { result = this.(Call).getArgument(0) }
91-
}
92-
93-
class EVP_Cipher_Final_Call extends EVP_Cipher_Operation {
59+
class EVP_Cipher_Final_Call extends EVPFinal, EVP_Cipher_Operation {
9460
EVP_Cipher_Final_Call() {
9561
this.(Call).getTarget().getName() in [
9662
"EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal",
9763
"EVP_DecryptFinal", "EVP_CipherFinal"
9864
]
9965
}
100-
101-
EVP_Cipher_Update_Call getUpdateCalls() {
102-
CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg())
103-
}
104-
105-
override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() }
106-
107-
override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }
108-
}
109-
110-
class EVP_PKEY_Operation extends EVP_Cipher_Operation {
111-
EVP_PKEY_Operation() {
112-
this.(Call).getTarget().getName() in ["EVP_PKEY_decrypt", "EVP_PKEY_encrypt"]
113-
}
114-
115-
override Expr getInputArg() { result = this.(Call).getArgument(3) }
116-
// TODO: how PKEY is initialized is different that symmetric cipher
117-
// Consider making an entirely new class for this and specializing
118-
// the get init call
119-
}
120-
121-
class EVPCipherInputArgument extends Expr {
122-
EVPCipherInputArgument() { exists(EVP_Cipher_Operation op | op.getInputArg() = this) }
12366
}

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import cpp
2+
private import OpenSSLOperationBase
23

3-
abstract class EVP_Hash_Initializer extends Call {
4-
Expr getContextArg() { result = this.(Call).getArgument(0) }
5-
6-
abstract Expr getAlgorithmArg();
7-
}
4+
abstract class EVP_Hash_Initializer extends EVPInitialize { }
85

96
class EVP_DigestInit_Variant_Calls extends EVP_Hash_Initializer {
107
EVP_DigestInit_Variant_Calls() {

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

Lines changed: 30 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,118 +8,78 @@ private import OpenSSLOperationBase
88
private import EVPHashInitializer
99
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
1010

11-
// import EVPHashConsumers
12-
abstract class EVP_Hash_Operation extends OpenSSLOperation, Crypto::HashOperationInstance {
13-
Expr getContextArg() { result = this.(Call).getArgument(0) }
14-
15-
Expr getAlgorithmArg() { result = this.getInitCall().getAlgorithmArg() }
16-
17-
EVP_Hash_Initializer getInitCall() {
18-
CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg())
19-
}
20-
21-
/**
22-
* By default, the algorithm value comes from the init call.
23-
* There are variants where this isn't true, in which case the
24-
* subclass should override this method.
25-
*/
26-
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
27-
AlgGetterToAlgConsumerFlow::flow(result.(OpenSSLAlgorithmValueConsumer).getResultNode(),
28-
DataFlow::exprNode(this.getAlgorithmArg()))
29-
}
30-
}
31-
32-
private module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig {
33-
predicate isSource(DataFlow::Node source) {
34-
exists(OpenSSLAlgorithmValueConsumer c | c.getResultNode() = source)
35-
}
11+
class EVP_Digest_Update_Call extends EVPUpdate {
12+
EVP_Digest_Update_Call() { this.(Call).getTarget().getName() in ["EVP_DigestUpdate"] }
3613

37-
predicate isSink(DataFlow::Node sink) {
38-
exists(EVP_Hash_Operation c | c.getAlgorithmArg() = sink.asExpr())
39-
}
14+
override Expr getInputArg() { result = this.(Call).getArgument(1) }
4015
}
4116

42-
private module AlgGetterToAlgConsumerFlow = DataFlow::Global<AlgGetterToAlgConsumerConfig>;
43-
4417
//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
45-
class EVP_Q_Digest_Operation extends EVP_Hash_Operation {
18+
class EVP_Q_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance {
4619
EVP_Q_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Q_digest" }
4720

48-
//override Crypto::AlgorithmConsumer getAlgorithmConsumer() { }
21+
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
22+
4923
override EVP_Hash_Initializer getInitCall() {
5024
// This variant of digest does not use an init
5125
// and even if it were used, the init would be ignored/undefined
5226
none()
5327
}
5428

55-
override Expr getOutputArg() { result = this.(Call).getArgument(5) }
56-
5729
override Expr getInputArg() { result = this.(Call).getArgument(3) }
5830

59-
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() }
60-
61-
override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }
31+
override Expr getOutputArg() { result = this.(Call).getArgument(5) }
6232

63-
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
64-
// The operation is a direct algorithm consumer
65-
// NOTE: the operation itself is already modeld as a value consumer, so we can
66-
// simply return 'this', see modeled hash algorithm consuers for EVP_Q_Digest
67-
this = result
33+
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
34+
result = EVPOneShot.super.getOutputArtifact()
6835
}
6936

70-
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
37+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
38+
result = EVPOneShot.super.getInputConsumer()
39+
}
7140
}
7241

73-
class EVP_Digest_Operation extends EVP_Hash_Operation {
42+
class EVP_Digest_Operation extends EVPOneShot, Crypto::HashOperationInstance {
7443
EVP_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Digest" }
7544

7645
// There is no context argument for this function
7746
override Expr getContextArg() { none() }
7847

48+
override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) }
49+
7950
override EVP_Hash_Initializer getInitCall() {
8051
// This variant of digest does not use an init
8152
// and even if it were used, the init would be ignored/undefined
8253
none()
8354
}
8455

85-
override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) }
86-
87-
override Expr getOutputArg() { result = this.(Call).getArgument(2) }
88-
8956
override Expr getInputArg() { result = this.(Call).getArgument(0) }
9057

91-
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() }
92-
93-
override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }
94-
}
95-
96-
// NOTE: not modeled as hash operations, these are intermediate calls
97-
class EVP_Digest_Update_Call extends Call {
98-
EVP_Digest_Update_Call() { this.(Call).getTarget().getName() in ["EVP_DigestUpdate"] }
99-
100-
Expr getInputArg() { result = this.(Call).getArgument(1) }
58+
override Expr getOutputArg() { result = this.(Call).getArgument(2) }
10159

102-
DataFlow::Node getInputNode() { result.asExpr() = this.getInputArg() }
60+
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
61+
result = EVPOneShot.super.getOutputArtifact()
62+
}
10363

104-
Expr getContextArg() { result = this.(Call).getArgument(0) }
64+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
65+
result = EVPOneShot.super.getInputConsumer()
66+
}
10567
}
10668

107-
class EVP_Digest_Final_Call extends EVP_Hash_Operation {
69+
class EVP_Digest_Final_Call extends EVPFinal, Crypto::HashOperationInstance {
10870
EVP_Digest_Final_Call() {
10971
this.(Call).getTarget().getName() in [
11072
"EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF"
11173
]
11274
}
11375

114-
EVP_Digest_Update_Call getUpdateCalls() {
115-
CTXFlow::ctxArgFlowsToCtxArg(result.getContextArg(), this.getContextArg())
116-
}
117-
118-
override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() }
119-
120-
override Crypto::ConsumerInputDataFlowNode getInputConsumer() { result = this.getInputNode() }
121-
12276
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
12377

124-
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { result = this.getOutputNode() }
78+
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
79+
result = EVPFinal.super.getOutputArtifact()
80+
}
81+
82+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
83+
result = EVPFinal.super.getInputConsumer()
84+
}
12585
}

0 commit comments

Comments
 (0)