Skip to content

Commit bec69ca

Browse files
committed
Refactor consumer and generic source model
1 parent 9cd0340 commit bec69ca

File tree

3 files changed

+859
-489
lines changed

3 files changed

+859
-489
lines changed

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

Lines changed: 225 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import java
22
import semmle.code.java.dataflow.DataFlow
3+
import semmle.code.java.dataflow.TaintTracking
34
import semmle.code.java.controlflow.Dominance
45

56
module JCAModel {
@@ -43,6 +44,22 @@ module JCAModel {
4344
.toUpperCase())
4445
}
4546

47+
bindingset[kdf]
48+
predicate kdf_names(string kdf) {
49+
kdf.toUpperCase().matches(["PBKDF2With%", "PBEWith%"].toUpperCase())
50+
}
51+
52+
bindingset[name]
53+
Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) {
54+
name.matches("PBKDF2With%") and
55+
result instanceof Crypto::PBKDF2 and
56+
withSubstring = name.regexpCapture("PBKDF2With(.*)", 1)
57+
or
58+
name.matches("PBEWith%") and
59+
result instanceof Crypto::PBES and
60+
withSubstring = name.regexpCapture("PBEWith(.*)", 1)
61+
}
62+
4663
bindingset[name]
4764
Crypto::THashType hash_name_to_hash_type(string name, int digestLength) {
4865
name = "SHA-1" and result instanceof Crypto::SHA1 and digestLength = 160
@@ -132,7 +149,7 @@ module JCAModel {
132149
}
133150
}
134151

135-
module AlgorithmStringToFetchFlow = DataFlow::Global<AlgorithmStringToFetchConfig>;
152+
module AlgorithmStringToFetchFlow = TaintTracking::Global<AlgorithmStringToFetchConfig>;
136153

137154
/**
138155
* Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable.
@@ -303,7 +320,7 @@ module JCAModel {
303320

304321
CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() }
305322

306-
override DataFlow::Node getInputNode() { result.asExpr() = this }
323+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
307324

308325
CipherStringLiteral getOrigin(string value) {
309326
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result),
@@ -447,13 +464,15 @@ module JCAModel {
447464

448465
override Crypto::CipherOperationSubtype getCipherOperationSubtype() { result = mode }
449466

450-
override DataFlow::Node getNonceConsumer() {
467+
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
451468
result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getNonceArg()
452469
}
453470

454-
override DataFlow::Node getInputConsumer() { result = doFinalize.getMessageArg() }
471+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
472+
result = doFinalize.getMessageArg()
473+
}
455474

456-
override DataFlow::Node getKeyConsumer() {
475+
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
457476
result.asExpr() = sink.getState().(InitializedCipherModeFlowState).getInitCall().getKeyArg()
458477
}
459478

@@ -611,7 +630,7 @@ module JCAModel {
611630
class CipherInitCallKeyConsumer extends Crypto::ArtifactConsumer {
612631
CipherInitCallKeyConsumer() { this = any(CipherInitCall call).getKeyArg() }
613632

614-
override DataFlow::Node getInputNode() { result.asExpr() = this }
633+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
615634
}
616635

617636
class CipherOperationCallOutput extends Crypto::CipherOutputArtifactInstance {
@@ -659,7 +678,7 @@ module JCAModel {
659678

660679
MessageDigestAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
661680

662-
override DataFlow::Node getInputNode() { result.asExpr() = this }
681+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
663682

664683
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
665684
exists(KnownHashAlgorithm l | l.getConsumer() = this and result = l)
@@ -719,7 +738,7 @@ module JCAModel {
719738

720739
KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
721740

722-
override DataFlow::Node getInputNode() { result.asExpr() = this }
741+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
723742

724743
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
725744
result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this
@@ -780,6 +799,10 @@ module JCAModel {
780799
Crypto::AlgorithmInstance getAKnownAlgorithm() {
781800
result = this.getAnAlgorithmValueConsumer().getAKnownAlgorithmSource()
782801
}
802+
803+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
804+
805+
override string getKeySizeFixed() { none() }
783806
}
784807

785808
/*
@@ -845,7 +868,9 @@ module JCAModel {
845868

846869
module MACInitCallToMACOperationFlowConfig implements DataFlow::ConfigSig {
847870
// TODO: use flow state with one config
848-
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof MACInitCall }
871+
predicate isSource(DataFlow::Node src) {
872+
exists(MACInitCall init | src.asExpr() = init.getQualifier())
873+
}
849874

850875
predicate isSink(DataFlow::Node sink) {
851876
exists(MACOperationCall call | sink.asExpr() = call.(MethodCall).getQualifier())
@@ -884,7 +909,7 @@ module JCAModel {
884909
}
885910

886911
MACInitCall getInitCall() {
887-
MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this),
912+
MACGetInstanceToMACOperationFlow::flow(DataFlow::exprNode(this),
888913
DataFlow::exprNode(result.getQualifier()))
889914
}
890915
}
@@ -897,7 +922,7 @@ module JCAModel {
897922
}
898923

899924
MACOperationCall getOperation() {
900-
MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this),
925+
MACInitCallToMACOperationFlow::flow(DataFlow::exprNode(this.getQualifier()),
901926
DataFlow::exprNode(result.(MethodCall).getQualifier()))
902927
}
903928
}
@@ -907,7 +932,7 @@ module JCAModel {
907932

908933
MACGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
909934

910-
override DataFlow::Node getInputNode() { result.asExpr() = this }
935+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
911936

912937
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
913938
exists(KnownMACAlgorithm l | l.getConsumer() = this and result = l)
@@ -933,7 +958,7 @@ module JCAModel {
933958
)
934959
}
935960

936-
override DataFlow::Node getKeyConsumer() {
961+
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
937962
exists(MACGetInstanceCall instantiation, MACInitCall initCall |
938963
instantiation.getOperation() = this and
939964
initCall.getOperation() = this and
@@ -942,9 +967,195 @@ module JCAModel {
942967
)
943968
}
944969

945-
override DataFlow::Node getMessageConsumer() {
970+
override Crypto::ConsumerInputDataFlowNode getMessageConsumer() {
946971
result.asExpr() = super.getArgument(0) and
947972
super.getMethod().getParameterType(0).hasName("byte[]")
948973
}
949974
}
975+
976+
module SecretKeyFactoryGetInstanceToGenerateSecretFlowConfig implements DataFlow::ConfigSig {
977+
predicate isSource(DataFlow::Node src) {
978+
exists(SecretKeyFactoryGetInstanceCall call | src.asExpr() = call)
979+
}
980+
981+
predicate isSink(DataFlow::Node sink) {
982+
exists(SecretKeyFactoryGenerateSecretCall call |
983+
sink.asExpr() = call.(MethodCall).getQualifier()
984+
)
985+
}
986+
}
987+
988+
module PBEKeySpecInstantiationToGenerateSecretFlowConfig implements DataFlow::ConfigSig {
989+
predicate isSource(DataFlow::Node src) {
990+
exists(PBEKeySpecInstantiation call | src.asExpr() = call)
991+
}
992+
993+
predicate isSink(DataFlow::Node sink) {
994+
exists(SecretKeyFactoryGenerateSecretCall call | sink.asExpr() = call.getKeySpecArg())
995+
}
996+
}
997+
998+
module KDFAlgorithmStringToGetInstanceConfig implements DataFlow::ConfigSig {
999+
predicate isSource(DataFlow::Node src) { kdf_names(src.asExpr().(StringLiteral).getValue()) }
1000+
1001+
predicate isSink(DataFlow::Node sink) {
1002+
exists(SecretKeyFactoryGetInstanceCall call | sink.asExpr() = call.getAlgorithmArg())
1003+
}
1004+
}
1005+
1006+
module SecretKeyFactoryGetInstanceToGenerateSecretFlow =
1007+
DataFlow::Global<SecretKeyFactoryGetInstanceToGenerateSecretFlowConfig>;
1008+
1009+
module PBEKeySpecInstantiationToGenerateSecretFlow =
1010+
DataFlow::Global<PBEKeySpecInstantiationToGenerateSecretFlowConfig>;
1011+
1012+
module KDFAlgorithmStringToGetInstanceFlow =
1013+
DataFlow::Global<KDFAlgorithmStringToGetInstanceConfig>;
1014+
1015+
class PBEKeySpecInstantiation extends ClassInstanceExpr {
1016+
PBEKeySpecInstantiation() {
1017+
this.getConstructedType().hasQualifiedName("javax.crypto.spec", "PBEKeySpec")
1018+
}
1019+
1020+
Expr getPasswordArg() { result = this.getArgument(0) }
1021+
1022+
Expr getSaltArg() { result = this.getArgument(1) }
1023+
1024+
Expr getIterationCountArg() { result = this.getArgument(2) }
1025+
1026+
Expr getKeyLengthArg() { result = this.getArgument(3) }
1027+
}
1028+
1029+
class SecretKeyFactoryGetInstanceCall extends MethodCall {
1030+
SecretKeyFactoryGetInstanceCall() {
1031+
this.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "getInstance")
1032+
}
1033+
1034+
Expr getAlgorithmArg() { result = this.getArgument(0) }
1035+
1036+
SecretKeyFactoryGenerateSecretCall getOperation() {
1037+
SecretKeyFactoryGetInstanceToGenerateSecretFlow::flow(DataFlow::exprNode(this),
1038+
DataFlow::exprNode(result.(MethodCall).getQualifier()))
1039+
}
1040+
}
1041+
1042+
class KDFAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral
1043+
{
1044+
SecretKeyFactoryKDFAlgorithmValueConsumer consumer;
1045+
1046+
KDFAlgorithmStringLiteral() {
1047+
kdf_names(this.getValue()) and
1048+
KDFAlgorithmStringToGetInstanceFlow::flow(DataFlow::exprNode(this), consumer.getInputNode())
1049+
}
1050+
1051+
override string getRawKDFAlgorithmName() { result = super.getValue() }
1052+
1053+
override Crypto::TKeyDerivationType getKDFType() {
1054+
result = kdf_name_to_kdf_type(super.getValue(), _)
1055+
}
1056+
1057+
SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer }
1058+
}
1059+
1060+
class PBKDF2AlgorithmStringLiteral extends KDFAlgorithmStringLiteral,
1061+
Crypto::PBKDF2AlgorithmInstance, Crypto::HMACAlgorithmInstance, Crypto::HashAlgorithmInstance,
1062+
Crypto::AlgorithmValueConsumer
1063+
{
1064+
PBKDF2AlgorithmStringLiteral() { super.getKDFType() instanceof Crypto::PBKDF2 }
1065+
1066+
override Crypto::ConsumerInputDataFlowNode getInputNode() { none() }
1067+
1068+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this }
1069+
1070+
override Crypto::THashType getHashFamily() {
1071+
result = hash_name_to_hash_type(this.getRawHashAlgorithmName(), _)
1072+
}
1073+
1074+
override int getDigestLength() {
1075+
exists(hash_name_to_hash_type(this.getRawHashAlgorithmName(), result))
1076+
}
1077+
1078+
override string getRawMACAlgorithmName() {
1079+
result = super.getRawKDFAlgorithmName().splitAt("PBKDF2With", 1)
1080+
}
1081+
1082+
override string getRawHashAlgorithmName() {
1083+
result = super.getRawKDFAlgorithmName().splitAt("WithHmac", 1)
1084+
}
1085+
1086+
override Crypto::TMACType getMACType() { result instanceof Crypto::THMAC }
1087+
1088+
override Crypto::AlgorithmValueConsumer getHMACAlgorithmValueConsumer() { result = this }
1089+
1090+
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { result = this }
1091+
}
1092+
1093+
class SecretKeyFactoryKDFAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof Expr
1094+
{
1095+
SecretKeyFactoryGetInstanceCall call;
1096+
1097+
SecretKeyFactoryKDFAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
1098+
1099+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
1100+
1101+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
1102+
exists(KDFAlgorithmStringLiteral l | l.getConsumer() = this and result = l)
1103+
}
1104+
1105+
SecretKeyFactoryGetInstanceCall getInstantiation() { result = call }
1106+
}
1107+
1108+
class SecretKeyFactoryGenerateSecretCall extends Crypto::KeyDerivationOperationInstance instanceof MethodCall
1109+
{
1110+
SecretKeyFactoryGenerateSecretCall() {
1111+
super.getCallee().hasQualifiedName("javax.crypto", "SecretKeyFactory", "generateSecret")
1112+
}
1113+
1114+
Expr getKeySpecArg() {
1115+
result = super.getArgument(0) and
1116+
super.getMethod().getParameterType(0).hasName("KeySpec")
1117+
}
1118+
1119+
PBEKeySpecInstantiation getInstantiation() {
1120+
PBEKeySpecInstantiationToGenerateSecretFlow::flow(DataFlow::exprNode(result),
1121+
DataFlow::exprNode(this.getKeySpecArg()))
1122+
}
1123+
1124+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
1125+
exists(SecretKeyFactoryGetInstanceCall instantiation |
1126+
instantiation.getOperation() = this and result = instantiation.getAlgorithmArg()
1127+
)
1128+
}
1129+
1130+
override Crypto::ConsumerInputDataFlowNode getSaltConsumer() {
1131+
result.asExpr() = this.getInstantiation().getSaltArg()
1132+
}
1133+
1134+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
1135+
result.asExpr() = this.getInstantiation().getPasswordArg()
1136+
}
1137+
1138+
override Crypto::ConsumerInputDataFlowNode getIterationCountConsumer() {
1139+
result.asExpr() = this.getInstantiation().getIterationCountArg()
1140+
}
1141+
1142+
override DataFlow::Node getOutputKeyArtifact() {
1143+
result.asExpr() = this and
1144+
super.getMethod().getReturnType().hasName("SecretKey")
1145+
}
1146+
1147+
override Crypto::ConsumerInputDataFlowNode getOutputKeySizeConsumer() {
1148+
result.asExpr() = this.getInstantiation().getKeyLengthArg()
1149+
}
1150+
1151+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
1152+
result.asExpr() = this.getInstantiation().getKeyLengthArg()
1153+
}
1154+
1155+
override string getKeySizeFixed() { none() }
1156+
1157+
override string getOutputKeySizeFixed() { none() }
1158+
1159+
override string getIterationCountFixed() { none() }
1160+
}
9501161
}

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource {
9494
override string getAdditionalDescription() { result = this.toString() }
9595
}
9696

97-
class ConstantDataSource extends Crypto::GenericConstantOrAllocationSource instanceof Literal {
97+
class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal {
9898
override DataFlow::Node getOutputNode() { result.asExpr() = this }
9999

100100
override predicate flowsTo(Crypto::FlowAwareElement other) {
@@ -141,7 +141,7 @@ module ArtifactUniversalFlow = DataFlow::Global<ArtifactUniversalFlowConfig>;
141141
*/
142142
module GenericDataSourceUniversalFlowConfig implements DataFlow::ConfigSig {
143143
predicate isSource(DataFlow::Node source) {
144-
source = any(Crypto::GenericDataSourceInstance i).getOutputNode()
144+
source = any(Crypto::GenericSourceInstance i).getOutputNode()
145145
}
146146

147147
predicate isSink(DataFlow::Node sink) {
@@ -189,7 +189,21 @@ module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig {
189189
}
190190
}
191191

192-
module GenericDataSourceUniversalFlow = DataFlow::Global<GenericDataSourceUniversalFlowConfig>;
192+
module GenericDataSourceUniversalFlow = TaintTracking::Global<GenericDataSourceUniversalFlowConfig>;
193+
194+
/*
195+
* class LiteralOrGenericDataSource extends Element {
196+
* DataFlow::Node node;
197+
*
198+
* LiteralOrGenericDataSource() {
199+
* node = this.(Crypto::GenericSourceInstance).getOutputNode() or
200+
* node.asExpr() = this.(Literal)
201+
* }
202+
*
203+
* bindingset[other]
204+
* predicate localFlowsTo(DataFlow::Node other) { DataFlow::localFlow(node, other) }
205+
* }
206+
*/
193207

194208
// Import library-specific modeling
195209
import JCA

0 commit comments

Comments
 (0)