Skip to content

Commit 95607c5

Browse files
committed
Refactor instances and consumers + add JCA hashes
1 parent 8a7671d commit 95607c5

File tree

10 files changed

+467
-159
lines changed

10 files changed

+467
-159
lines changed

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

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import semmle.code.cpp.ir.IR
33
import semmle.code.cpp.security.FlowSources as FlowSources
44
private import cpp as Lang
55

6-
76
module CryptoInput implements InputSig<Lang::Location> {
87
class DataFlowNode = DataFlow::Node;
8+
99
class LocatableElement = Lang::Locatable;
10+
1011
class UnknownLocation = Lang::UnknownDefaultLocation;
1112
}
1213

@@ -21,7 +22,6 @@ abstract class AdditionalFlowInputStep extends DataFlow::Node {
2122
final DataFlow::Node getInput() { result = this }
2223
}
2324

24-
2525
/**
2626
* Generic data source to node input configuration
2727
*/
@@ -47,61 +47,44 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig {
4747
}
4848
}
4949

50-
51-
5250
// // // TODO: I think this will be inefficient, no?
5351
// // class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal {
54-
// // override DataFlow::Node getOutputNode() {
55-
// // result.asExpr() = this
52+
// // override DataFlow::Node getOutputNode() {
53+
// // result.asExpr() = this
5654
// // }
57-
5855
// // override predicate flowsTo(Crypto::FlowAwareElement other) {
5956
// // // TODO: separate config to avoid blowing up data-flow analysis
6057
// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
6158
// // }
62-
6359
// // override string getAdditionalDescription() { result = this.toString() }
6460
// // }
65-
6661
// /**
6762
// * Definitions of various generic data sources
6863
// */
6964
// // final class DefaultFlowSource = SourceNode;
70-
7165
// // final class DefaultRemoteFlowSource = RemoteFlowSource;
72-
7366
// // class GenericLocalDataSource extends Crypto::GenericLocalDataSource {
7467
// // GenericLocalDataSource() {
7568
// // any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this
7669
// // }
77-
7870
// // override DataFlow::Node getOutputNode() { result.asExpr() = this }
79-
8071
// // override predicate flowsTo(Crypto::FlowAwareElement other) {
8172
// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
8273
// // }
83-
8474
// // override string getAdditionalDescription() { result = this.toString() }
8575
// // }
86-
8776
// // class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource {
8877
// // GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this }
89-
9078
// // override DataFlow::Node getOutputNode() { result.asExpr() = this }
91-
9279
// // override predicate flowsTo(Crypto::FlowAwareElement other) {
9380
// // GenericDataSourceUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
9481
// // }
95-
9682
// // override string getAdditionalDescription() { result = this.toString() }
9783
// // }
98-
99-
10084
// module GenericDataSourceUniversalFlow = DataFlow::Global<GenericDataSourceUniversalFlowConfig>;
101-
10285
module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig {
10386
predicate isSource(DataFlow::Node source) {
104-
source = any(Crypto::ArtifactElement artifact).getOutputNode()
87+
source = any(Crypto::ArtifactInstance artifact).getOutputNode()
10588
}
10689

10790
predicate isSink(DataFlow::Node sink) {
@@ -120,12 +103,13 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig {
120103
node1.(AdditionalFlowInputStep).getOutput() = node2
121104
}
122105
}
106+
123107
module ArtifactUniversalFlow = DataFlow::Global<ArtifactUniversalFlowConfig>;
108+
124109
abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance {
125110
override predicate flowsTo(Crypto::FlowAwareElement other) {
126111
ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
127112
}
128113
}
129114

130-
131115
import OpenSSL.OpenSSL

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class EVP_Cipher_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsume
66
{
77
override DataFlow::Node getInputNode() { result.asExpr() = this }
88

9-
override Crypto::AlgorithmElement getAKnownAlgorithmSource() {
9+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
1010
result.(KnownOpenSSLCipherConstantAlgorithmInstance).getConsumer() = this
1111
}
1212
}

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

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,29 @@ import EVPHashInitializer
22
import EVPHashOperation
33
import EVPHashAlgorithmSource
44

5-
class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVPDigestInitializerAlgorithmArgument{
6-
override DataFlow::Node getInputNode() { result.asExpr() = this }
5+
class EVP_Digest_Initializer_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVPDigestInitializerAlgorithmArgument
6+
{
7+
override DataFlow::Node getInputNode() { result.asExpr() = this }
78

8-
override Crypto::AlgorithmElement getAKnownAlgorithmSource() {
9-
result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this
10-
}
9+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
10+
result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this
11+
}
1112
}
1213

13-
class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVP_Q_Digest_Algorithm_Argument{
14-
override DataFlow::Node getInputNode() { result.asExpr() = this }
14+
class EVP_Q_Digest_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVP_Q_Digest_Algorithm_Argument
15+
{
16+
override DataFlow::Node getInputNode() { result.asExpr() = this }
1517

16-
override Crypto::AlgorithmElement getAKnownAlgorithmSource() {
17-
result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this
18-
}
18+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
19+
result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this
20+
}
1921
}
2022

21-
class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmConsumer instanceof EVP_Digest_Algorithm_Argument{
22-
override DataFlow::Node getInputNode() { result.asExpr() = this }
23+
class EVP_Digest_Algorithm_Consumer extends Crypto::AlgorithmValueConsumer instanceof EVP_Digest_Algorithm_Argument
24+
{
25+
override DataFlow::Node getInputNode() { result.asExpr() = this }
2326

24-
override Crypto::AlgorithmElement getAKnownAlgorithmSource() {
25-
result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this
26-
}
27-
}
27+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
28+
result.(KnownOpenSSLHashConstantAlgorithmInstance).getConsumer() = this
29+
}
30+
}

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

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ module JCAModel {
205205
*
206206
* For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`.
207207
*/
208-
class CipherGetInstanceAlgorithmArg extends Crypto::AlgorithmConsumer instanceof Expr {
208+
class CipherGetInstanceAlgorithmArg extends Crypto::AlgorithmValueConsumer instanceof Expr {
209209
CipherGetInstanceCall call;
210210

211211
CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() }
@@ -218,7 +218,7 @@ module JCAModel {
218218
value = result.getValue()
219219
}
220220

221-
override Crypto::AlgorithmElement getAKnownAlgorithmSource() {
221+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
222222
result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this
223223
}
224224
}
@@ -354,15 +354,17 @@ module JCAModel {
354354

355355
override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode }
356356

357-
override Crypto::NonceArtifactConsumer getNonceConsumer() {
358-
result = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg()
357+
override DataFlow::Node getNonceConsumer() {
358+
result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg()
359359
}
360360

361-
override Crypto::CipherInputConsumer getInputConsumer() {
362-
result = doFinalize.getMessageArg().asExpr()
361+
override DataFlow::Node getInputConsumer() { result = doFinalize.getMessageArg() }
362+
363+
override DataFlow::Node getKeyConsumer() {
364+
result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getKeyArg()
363365
}
364366

365-
override Crypto::AlgorithmConsumer getAlgorithmConsumer() { result = consumer }
367+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = consumer }
366368

367369
override Crypto::CipherOutputArtifactInstance getOutputArtifact() {
368370
result = doFinalize.getOutput()
@@ -493,27 +495,114 @@ module JCAModel {
493495
}
494496
}
495497

496-
class CipherInitCallNonceArgConsumer extends Crypto::NonceArtifactConsumer instanceof Expr {
497-
CipherInitCallNonceArgConsumer() { this = any(CipherInitCall call).getNonceArg() }
498+
class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer {
499+
CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() }
498500

499501
override DataFlow::Node getInputNode() { result.asExpr() = this }
500502
}
501503

502-
class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer {
503-
CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() }
504+
class CipherOperationCallOutput extends CipherOutputArtifact {
505+
CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() }
504506

505-
override DataFlow::Node getInputNode() { result.asExpr() = this }
507+
override DataFlow::Node getOutputNode() { result.asExpr() = this }
508+
}
509+
510+
bindingset[hash]
511+
predicate hash_names(string hash) {
512+
hash.toUpperCase()
513+
.matches([
514+
"SHA-1", "SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384",
515+
"SHA3-512", "BLAKE2b", "BLAKE2s"
516+
].toUpperCase())
517+
}
518+
519+
// flow config from a known hash algorithm literal to MessageDigest.getInstance
520+
module KnownHashAlgorithmLiteralToMessageDigestConfig implements DataFlow::ConfigSig {
521+
predicate isSource(DataFlow::Node src) { hash_names(src.asExpr().(StringLiteral).getValue()) }
522+
523+
predicate isSink(DataFlow::Node sink) {
524+
exists(MessageDigestGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg())
525+
}
526+
}
527+
528+
module KnownHashAlgorithmLiteralToMessageDigestFlow =
529+
DataFlow::Global<KnownHashAlgorithmLiteralToMessageDigestConfig>;
530+
531+
class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral {
532+
MessageDigestAlgorithmValueConsumer consumer;
533+
534+
KnownHashAlgorithm() {
535+
hash_names(this.getValue()) and
536+
KnownHashAlgorithmLiteralToMessageDigestFlow::flow(DataFlow::exprNode(this),
537+
consumer.getInputNode())
538+
}
539+
540+
MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer }
541+
542+
override string getRawAlgorithmName() { result = this.(StringLiteral).getValue() }
543+
544+
override Crypto::THashType getHashFamily() {
545+
result = Crypto::OtherHashType() // TODO
546+
}
506547
}
507548

508-
class CipherMessageInputConsumer extends Crypto::CipherInputConsumer {
509-
CipherMessageInputConsumer() { this = any(CipherOperationCall call).getMessageArg().asExpr() }
549+
class MessageDigestAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
550+
MessageDigestGetInstanceCall call;
551+
552+
MessageDigestAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
510553

511554
override DataFlow::Node getInputNode() { result.asExpr() = this }
555+
556+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
557+
exists(KnownHashAlgorithm l | l.getConsumer() = this and result = l)
558+
}
512559
}
513560

514-
class CipherOperationCallOutput extends CipherOutputArtifact {
515-
CipherOperationCallOutput() { this = any(CipherOperationCall call).getOutput() }
561+
class MessageDigestGetInstanceCall extends MethodCall {
562+
MessageDigestGetInstanceCall() {
563+
this.getCallee().hasQualifiedName("java.security", "MessageDigest", "getInstance")
564+
}
565+
566+
Expr getAlgorithmArg() { result = this.getArgument(0) }
567+
568+
DigestHashOperation getDigestCall() {
569+
DigestGetInstanceToDigestFlow::flow(DataFlow::exprNode(this),
570+
DataFlow::exprNode(result.(DigestCall).getQualifier()))
571+
}
572+
}
573+
574+
class DigestCall extends MethodCall {
575+
DigestCall() { this.getCallee().hasQualifiedName("java.security", "MessageDigest", "digest") }
576+
577+
Expr getDigestArtifactOutput() { result = this }
578+
}
579+
580+
// flow config from MessageDigest.getInstance to MessageDigest.digest
581+
module DigestGetInstanceToDigestConfig implements DataFlow::ConfigSig {
582+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MessageDigestGetInstanceCall }
583+
584+
predicate isSink(DataFlow::Node sink) {
585+
exists(DigestCall c | c.getQualifier() = sink.asExpr())
586+
}
587+
}
588+
589+
module DigestGetInstanceToDigestFlow = DataFlow::Global<DigestGetInstanceToDigestConfig>;
590+
591+
class DigestArtifact extends DigestArtifactInstance {
592+
DigestArtifact() { this = any(DigestCall call).getDigestArtifactOutput() }
516593

517594
override DataFlow::Node getOutputNode() { result.asExpr() = this }
518595
}
596+
597+
class DigestHashOperation extends Crypto::HashOperationInstance instanceof DigestCall {
598+
override Crypto::DigestArtifactInstance getDigestArtifact() {
599+
result = this.(DigestCall).getDigestArtifactOutput()
600+
}
601+
602+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
603+
exists(MessageDigestGetInstanceCall call |
604+
call.getDigestCall() = this and result = call.getAlgorithmArg()
605+
)
606+
}
607+
}
519608
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ module CryptoInput implements InputSig<Language::Location> {
2525
class LocatableElement = Language::Element;
2626

2727
class UnknownLocation = UnknownDefaultLocation;
28+
29+
LocatableElement dfn_to_element(DataFlow::Node node) {
30+
result = node.asExpr() or
31+
result = node.asParameter()
32+
}
2833
}
2934

3035
/**
@@ -100,6 +105,17 @@ class InsecureRandomnessInstance extends RandomnessInstance {
100105
InsecureRandomnessInstance() { exists(InsecureRandomnessSource node | this = node.asExpr()) }
101106
}
102107

108+
/**
109+
* Output artifact flow logic
110+
*/
111+
abstract class DigestArtifactInstance extends Crypto::DigestArtifactInstance {
112+
override predicate flowsTo(Crypto::FlowAwareElement other) {
113+
ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
114+
}
115+
116+
override predicate isConsumerArtifact() { none() }
117+
}
118+
103119
/**
104120
* Artifact output to node input configuration
105121
*/
@@ -115,6 +131,8 @@ abstract class CipherOutputArtifact extends Crypto::CipherOutputArtifactInstance
115131
override predicate flowsTo(Crypto::FlowAwareElement other) {
116132
ArtifactUniversalFlow::flow(this.getOutputNode(), other.getInputNode())
117133
}
134+
135+
override predicate isConsumerArtifact() { none() }
118136
}
119137

120138
/**
@@ -144,7 +162,7 @@ module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig {
144162

145163
module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig {
146164
predicate isSource(DataFlow::Node source) {
147-
source = any(Crypto::ArtifactElement artifact).getOutputNode()
165+
source = any(Crypto::ArtifactInstance artifact).getOutputNode()
148166
}
149167

150168
predicate isSink(DataFlow::Node sink) {

0 commit comments

Comments
 (0)