Skip to content

Commit 69429a3

Browse files
authored
Merge pull request #9 from nicolaswill/brodes/elliptic_curves
Misc. modifications to support elliptic curves and hooking them up to…
2 parents 5050758 + 697c9f0 commit 69429a3

File tree

3 files changed

+340
-79
lines changed

3 files changed

+340
-79
lines changed

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

Lines changed: 186 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import semmle.code.java.controlflow.Dominance
66
module JCAModel {
77
import Language
88

9+
abstract class JCAAlgorithmInstance extends Crypto::AlgorithmInstance {
10+
abstract Crypto::AlgorithmValueConsumer getConsumer();
11+
}
12+
913
// TODO: Verify that the PBEWith% case works correctly
1014
bindingset[algo]
1115
predicate cipher_names(string algo) {
@@ -49,6 +53,9 @@ module JCAModel {
4953
kdf.toUpperCase().matches(["PBKDF2With%", "PBEWith%"].toUpperCase())
5054
}
5155

56+
bindingset[name]
57+
predicate elliptic_curve_names(string name) { Crypto::isEllipticCurveAlgorithmName(name) }
58+
5259
bindingset[name]
5360
Crypto::TKeyDerivationType kdf_name_to_kdf_type(string name, string withSubstring) {
5461
name.matches("PBKDF2With%") and
@@ -110,6 +117,12 @@ module JCAModel {
110117
string getPadding() { result = this.getValue().splitAt("/", 2) }
111118
}
112119

120+
class EllipticCurveStringLiteral extends StringLiteral {
121+
EllipticCurveStringLiteral() { elliptic_curve_names(this.getValue()) }
122+
123+
string getStandardEllipticCurveName() { result = this.getValue() }
124+
}
125+
113126
class CipherGetInstanceCall extends Call {
114127
CipherGetInstanceCall() {
115128
this.getCallee().hasQualifiedName("javax.crypto", "Cipher", "getInstance")
@@ -139,17 +152,32 @@ module JCAModel {
139152
}
140153

141154
/**
142-
* Data-flow configuration modelling flow from a cipher string literal to a `CipherGetInstanceCall` argument.
155+
* Data-flow configuration modelling flow from a cipher string literal to a value consumer argument.
143156
*/
144-
private module AlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
157+
private module CipherAlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
145158
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral }
146159

147160
predicate isSink(DataFlow::Node sink) {
148161
exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode())
149162
}
150163
}
151164

152-
module AlgorithmStringToFetchFlow = TaintTracking::Global<AlgorithmStringToFetchConfig>;
165+
module CipherAlgorithmStringToFetchFlow =
166+
TaintTracking::Global<CipherAlgorithmStringToFetchConfig>;
167+
168+
/**
169+
* Data-flow configuration modelling flow from a cipher string literal to a value consumer argument.
170+
*/
171+
private module EllipticCurveAlgorithmStringToFetchConfig implements DataFlow::ConfigSig {
172+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof EllipticCurveStringLiteral }
173+
174+
predicate isSink(DataFlow::Node sink) {
175+
exists(Crypto::AlgorithmValueConsumer consumer | sink = consumer.getInputNode())
176+
}
177+
}
178+
179+
module EllipticCurveAlgorithmStringToFetchFlow =
180+
TaintTracking::Global<EllipticCurveAlgorithmStringToFetchConfig>;
153181

154182
/**
155183
* Note: padding and a mode of operation will only exist when the padding / mode (*and its type*) are determinable.
@@ -160,8 +188,8 @@ module JCAModel {
160188
*
161189
* TODO: Model the case of relying on a provider default, but alert on it as a bad practice.
162190
*/
163-
class CipherStringLiteralPaddingAlgorithmInstance extends CipherStringLiteralAlgorithmInstance,
164-
Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral
191+
class CipherStringLiteralPaddingAlgorithmInstance extends JCAAlgorithmInstance,
192+
CipherStringLiteralAlgorithmInstance, Crypto::PaddingAlgorithmInstance instanceof CipherStringLiteral
165193
{
166194
CipherStringLiteralPaddingAlgorithmInstance() { exists(super.getPadding()) } // TODO: provider defaults
167195

@@ -183,8 +211,8 @@ module JCAModel {
183211
}
184212
}
185213

186-
class CipherStringLiteralModeAlgorithmInstance extends CipherStringLiteralPaddingAlgorithmInstance,
187-
Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral
214+
class CipherStringLiteralModeAlgorithmInstance extends JCAAlgorithmInstance,
215+
CipherStringLiteralPaddingAlgorithmInstance, Crypto::ModeOfOperationAlgorithmInstance instanceof CipherStringLiteral
188216
{
189217
CipherStringLiteralModeAlgorithmInstance() { exists(super.getMode()) } // TODO: provider defaults
190218

@@ -216,15 +244,141 @@ module JCAModel {
216244
}
217245
}
218246

219-
class CipherStringLiteralAlgorithmInstance extends Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral
247+
class KeyGeneratorGetInstanceCall extends MethodCall {
248+
KeyGeneratorGetInstanceCall() {
249+
this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance")
250+
or
251+
this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance")
252+
}
253+
254+
Expr getAlgorithmArg() { result = super.getArgument(0) }
255+
}
256+
257+
// For general elliptic curves, getInstance("EC") is used
258+
// and java.security.spec.ECGenParameterSpec("<CURVE NAME>") is what sets the specific curve.
259+
// The result of ECGenParameterSpec is passed to KeyPairGenerator.initialize
260+
// If the curve is not specified, the default is used.
261+
// We would trace the use of this inside a KeyPairGenerator.initialize
262+
class ECGenParameterSpecCall extends ClassInstanceExpr {
263+
ECGenParameterSpecCall() {
264+
this.(ClassInstanceExpr)
265+
.getConstructedType()
266+
.hasQualifiedName("java.security.spec", "ECGenParameterSpec")
267+
}
268+
269+
Expr getAlgorithmArg() { result = super.getArgument(0) }
270+
271+
KeyPairGeneratorInitializeCall getInitializeConsumerCall() {
272+
exists(DataFlow::Node sink |
273+
ECGenParameterSpecCallToInitializeFlow::flow(DataFlow::exprNode(this), sink) and
274+
result.getAnArgument() = sink.asExpr()
275+
)
276+
}
277+
}
278+
279+
abstract class KeyGenAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
280+
// abstract predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink);
281+
abstract DataFlow::Node getResultNode();
282+
}
283+
284+
class KeyPairGeneratorInitializeCall extends MethodCall {
285+
KeyPairGeneratorInitializeCall() {
286+
this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "initialize")
287+
}
288+
289+
Expr getKeyArg() {
290+
result = this.getArgument(0) and
291+
result.getType() instanceof IntegralType
292+
}
293+
}
294+
295+
private module ECGenParameterSpecCallToInitializeConfig implements DataFlow::ConfigSig {
296+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ECGenParameterSpecCall }
297+
298+
predicate isSink(DataFlow::Node sink) {
299+
exists(KeyPairGeneratorInitializeCall c | c.getAnArgument() = sink.asExpr())
300+
}
301+
}
302+
303+
module ECGenParameterSpecCallToInitializeFlow =
304+
DataFlow::Global<ECGenParameterSpecCallToInitializeConfig>;
305+
306+
class ECGenParameterSpecAlgorithmValueConsumer extends KeyGenAlgorithmValueConsumer {
307+
ECGenParameterSpecCall call;
308+
309+
ECGenParameterSpecAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
310+
311+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
312+
313+
override DataFlow::Node getResultNode() {
314+
// Traversing to the initialilzer directly and calling this the 'result'
315+
// to simplify the trace. In theory you would trace from the call
316+
// through the initializer, but we already have a trace to the initializer
317+
// so using this instead of altering/creating data flow configs.
318+
call.getInitializeConsumerCall().getQualifier() = result.asExpr()
319+
}
320+
321+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
322+
result.(JCAAlgorithmInstance).getConsumer() = this
323+
}
324+
}
325+
326+
class KeyGeneratorGetInstanceAlgorithmValueConsumer extends KeyGenAlgorithmValueConsumer {
327+
KeyGeneratorGetInstanceCall call;
328+
329+
KeyGeneratorGetInstanceAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
330+
331+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
332+
333+
override DataFlow::Node getResultNode() { result.asExpr() = call }
334+
335+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
336+
// The source is any instance whose consumer is this
337+
result.(JCAAlgorithmInstance).getConsumer() = this
338+
}
339+
}
340+
341+
class EllipticCurveStringLiteralAlgorithmInstance extends JCAAlgorithmInstance,
342+
Crypto::EllipticCurveAlgorithmInstance instanceof StringLiteral
343+
{
344+
Crypto::AlgorithmValueConsumer consumer;
345+
346+
EllipticCurveStringLiteralAlgorithmInstance() {
347+
// Trace a known elliptic curve algorithm string literal to a key gen consumer
348+
EllipticCurveAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this),
349+
consumer.getInputNode())
350+
}
351+
352+
override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
353+
354+
override string getRawEllipticCurveAlgorithmName() { result = super.getValue() }
355+
356+
override string getStandardCurveName() { result = this.getRawEllipticCurveAlgorithmName() }
357+
358+
override Crypto::TEllipticCurveType getEllipticCurveFamily() {
359+
Crypto::isEllipticCurveAlgorithm(this.getRawEllipticCurveAlgorithmName(), _, result)
360+
}
361+
362+
override string getKeySize() {
363+
exists(int keySize |
364+
Crypto::isEllipticCurveAlgorithm(this.getRawEllipticCurveAlgorithmName(), keySize, _) and
365+
result = keySize.toString()
366+
)
367+
}
368+
}
369+
370+
class CipherStringLiteralAlgorithmInstance extends JCAAlgorithmInstance,
371+
Crypto::CipherAlgorithmInstance instanceof CipherStringLiteral
220372
{
373+
// NOTE: this consumer is generic, but cipher algorithms can be consumed
374+
// by getInstance as well as key generation
221375
Crypto::AlgorithmValueConsumer consumer;
222376

223377
CipherStringLiteralAlgorithmInstance() {
224-
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode())
378+
CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(this), consumer.getInputNode())
225379
}
226380

227-
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
381+
override Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
228382

229383
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() {
230384
result = this // TODO: provider defaults
@@ -302,8 +456,8 @@ module JCAModel {
302456
override int getDigestLength() { exists(hash_name_to_hash_type(hashName, result)) }
303457
}
304458

305-
class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance,
306-
CipherStringLiteralPaddingAlgorithmInstance
459+
class OAEPPaddingAlgorithmInstance extends JCAAlgorithmInstance,
460+
Crypto::OAEPPaddingAlgorithmInstance, CipherStringLiteralPaddingAlgorithmInstance
307461
{
308462
override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() { result = this }
309463

@@ -323,7 +477,7 @@ module JCAModel {
323477
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
324478

325479
CipherStringLiteral getOrigin(string value) {
326-
AlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result),
480+
CipherAlgorithmStringToFetchFlow::flow(DataFlow::exprNode(result),
327481
DataFlow::exprNode(this.(Expr).getAChildExpr*())) and
328482
value = result.getValue()
329483
}
@@ -651,7 +805,8 @@ module JCAModel {
651805
module KnownHashAlgorithmLiteralToMessageDigestFlow =
652806
DataFlow::Global<KnownHashAlgorithmLiteralToMessageDigestConfig>;
653807

654-
class KnownHashAlgorithm extends Crypto::HashAlgorithmInstance instanceof StringLiteral {
808+
class KnownHashAlgorithm extends JCAAlgorithmInstance, Crypto::HashAlgorithmInstance instanceof StringLiteral
809+
{
655810
MessageDigestAlgorithmValueConsumer consumer;
656811

657812
KnownHashAlgorithm() {
@@ -660,7 +815,7 @@ module JCAModel {
660815
consumer.getInputNode())
661816
}
662817

663-
MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer }
818+
override MessageDigestAlgorithmValueConsumer getConsumer() { result = consumer }
664819

665820
override string getRawHashAlgorithmName() { result = this.(StringLiteral).getValue() }
666821

@@ -733,46 +888,19 @@ module JCAModel {
733888
}
734889
}
735890

736-
class KeyGeneratorCallAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
737-
KeyGeneratorGetInstanceCall call;
738-
739-
KeyGeneratorCallAlgorithmValueConsumer() { this = call.getAlgorithmArg() }
740-
741-
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
742-
743-
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
744-
result.(CipherStringLiteralAlgorithmInstance).getConsumer() = this
745-
}
746-
}
747-
748891
// flow from instance created by getInstance to generateKey
749-
module KeyGeneratorGetInstanceToGenerateConfig implements DataFlow::ConfigSig {
892+
module KeyGeneratorAlgValueConsumerToGenerateConfig implements DataFlow::ConfigSig {
750893
predicate isSource(DataFlow::Node src) {
751-
exists(KeyGeneratorGetInstanceCall call | src.asExpr() = call)
894+
exists(KeyGenAlgorithmValueConsumer consumer | consumer.getResultNode() = src)
752895
}
753896

754897
predicate isSink(DataFlow::Node sink) {
755898
exists(KeyGeneratorGenerateCall call | sink.asExpr() = call.(MethodCall).getQualifier())
756899
}
757900
}
758901

759-
module KeyGeneratorGetInstanceToGenerateFlow =
760-
DataFlow::Global<KeyGeneratorGetInstanceToGenerateConfig>;
761-
762-
class KeyGeneratorGetInstanceCall extends MethodCall {
763-
KeyGeneratorGetInstanceCall() {
764-
this.getCallee().hasQualifiedName("javax.crypto", "KeyGenerator", "getInstance")
765-
or
766-
this.getCallee().hasQualifiedName("java.security", "KeyPairGenerator", "getInstance")
767-
}
768-
769-
Expr getAlgorithmArg() { result = super.getArgument(0) }
770-
771-
predicate flowsToKeyGenerateCallQualifier(KeyGeneratorGenerateCall sink) {
772-
KeyGeneratorGetInstanceToGenerateFlow::flow(DataFlow::exprNode(this),
773-
DataFlow::exprNode(sink.(MethodCall).getQualifier()))
774-
}
775-
}
902+
module KeyGeneratorAlgValueConsumerToGenerateFlow =
903+
DataFlow::Global<KeyGeneratorAlgValueConsumerToGenerateConfig>;
776904

777905
class KeyGeneratorGenerateCall extends Crypto::KeyGenerationOperationInstance instanceof MethodCall
778906
{
@@ -791,8 +919,10 @@ module JCAModel {
791919
override Crypto::KeyArtifactType getOutputKeyType() { result = type }
792920

793921
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
794-
exists(KeyGeneratorGetInstanceCall getInstance |
795-
getInstance.flowsToKeyGenerateCallQualifier(this) and result = getInstance.getAlgorithmArg()
922+
exists(KeyGenAlgorithmValueConsumer consumer |
923+
KeyGeneratorAlgValueConsumerToGenerateFlow::flow(consumer.getResultNode(),
924+
DataFlow::exprNode(this.(Call).getQualifier())) and
925+
result = consumer
796926
)
797927
}
798928

@@ -879,15 +1009,16 @@ module JCAModel {
8791009

8801010
module MACInitCallToMACOperationFlow = DataFlow::Global<MACInitCallToMACOperationFlowConfig>;
8811011

882-
class KnownMACAlgorithm extends Crypto::MACAlgorithmInstance instanceof StringLiteral {
1012+
class KnownMACAlgorithm extends JCAAlgorithmInstance, Crypto::MACAlgorithmInstance instanceof StringLiteral
1013+
{
8831014
MACGetInstanceAlgorithmValueConsumer consumer;
8841015

8851016
KnownMACAlgorithm() {
8861017
mac_names(this.getValue()) and
8871018
MACKnownAlgorithmToConsumerFlow::flow(DataFlow::exprNode(this), consumer.getInputNode())
8881019
}
8891020

890-
MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer }
1021+
override MACGetInstanceAlgorithmValueConsumer getConsumer() { result = consumer }
8911022

8921023
override string getRawMACAlgorithmName() { result = super.getValue() }
8931024

@@ -1039,7 +1170,8 @@ module JCAModel {
10391170
}
10401171
}
10411172

1042-
class KDFAlgorithmStringLiteral extends Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral
1173+
class KDFAlgorithmStringLiteral extends JCAAlgorithmInstance,
1174+
Crypto::KeyDerivationAlgorithmInstance instanceof StringLiteral
10431175
{
10441176
SecretKeyFactoryKDFAlgorithmValueConsumer consumer;
10451177

@@ -1054,10 +1186,10 @@ module JCAModel {
10541186
result = kdf_name_to_kdf_type(super.getValue(), _)
10551187
}
10561188

1057-
SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer }
1189+
override SecretKeyFactoryKDFAlgorithmValueConsumer getConsumer() { result = consumer }
10581190
}
10591191

1060-
class PBKDF2AlgorithmStringLiteral extends KDFAlgorithmStringLiteral,
1192+
class PBKDF2AlgorithmStringLiteral extends JCAAlgorithmInstance, KDFAlgorithmStringLiteral,
10611193
Crypto::PBKDF2AlgorithmInstance, Crypto::HMACAlgorithmInstance, Crypto::HashAlgorithmInstance,
10621194
Crypto::AlgorithmValueConsumer
10631195
{
@@ -1158,4 +1290,5 @@ module JCAModel {
11581290

11591291
override string getIterationCountFixed() { none() }
11601292
}
1293+
// TODO: flow the GCGenParametersSpecCall to an init, and the init to the operations
11611294
}

0 commit comments

Comments
 (0)