Skip to content

Commit 8d2ec44

Browse files
committed
Added support for BouncyCast ECDSA
This commit adds support for ECDSA. This includes tracking the instantiated curve parameters using data flow. It also adds SignatureArtifactInstance and SignatureOperationInstance types to the shared model.
1 parent 06fa0e9 commit 8d2ec44

File tree

4 files changed

+452
-38
lines changed

4 files changed

+452
-38
lines changed

java/ql/lib/experimental/quantum/BouncyCastle.qll

Lines changed: 207 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,101 @@
11
import java
22

3+
module Params {
4+
import Language
5+
import BouncyCastle.FlowAnalysis
6+
import BouncyCastle.AlgorithmInstances
7+
8+
/**
9+
* A model of the `Parameters` class in Bouncy Castle.
10+
*/
11+
class Parameters extends RefType {
12+
Parameters() {
13+
// Matches `org.bouncycastle.crypto.params`, `org.bouncycastle.asn1.x9`, etc.
14+
this.getPackage().getName().matches("org.bouncycastle.%") and
15+
this.getName().matches("%Parameters")
16+
}
17+
}
18+
19+
class KeyParameters extends Parameters {
20+
KeyParameters() {
21+
this.getPackage().getName() = "org.bouncycastle.crypto.params" and
22+
this.getName().matches("%KeyParameters")
23+
}
24+
}
25+
26+
/**
27+
* Any call that returns a BouncyCastle parameters object. This type is used
28+
* to model data flow to resolve algorithm instances like elliptic curves.
29+
*
30+
* Examples:
31+
* ```
32+
* curveParams = SECNamedCurves.getByName(...);
33+
* domainParams = new ECDomainParameters(...);
34+
* ```
35+
*/
36+
class ParametersInstantiation extends Call {
37+
ParametersInstantiation() {
38+
// Class instantiations
39+
this.(ConstructorCall)
40+
.getConstructedType()
41+
.getPackage()
42+
.getName()
43+
.matches("org.bouncycastle.%") and
44+
this.(ConstructorCall).getConstructedType() instanceof Parameters
45+
or
46+
// (Static) factory methods
47+
this.(MethodCall)
48+
.getCallee()
49+
.getDeclaringType()
50+
.getPackage()
51+
.getName()
52+
.matches("org.bouncycastle.%") and
53+
this.(MethodCall).getType() instanceof Parameters
54+
}
55+
56+
// Can be overridden by subclasses which take a key size argument.
57+
Expr getKeySizeArg() { none() }
58+
59+
Crypto::ConsumerInputDataFlowNode getAKeySizeConsumer() {
60+
result.asExpr() = this.getKeySizeArg()
61+
}
62+
63+
// Can be overridden by subclasses which take an algorithm argument.
64+
Expr getAlgorithmArg() { none() }
65+
66+
Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
67+
result.getInputNode().asExpr() = this.getAlgorithmArg()
68+
}
69+
70+
Expr getAParametersArg() {
71+
result = this.getAnArgument() and
72+
result.getType() instanceof Parameters
73+
}
74+
75+
Crypto::ConsumerInputDataFlowNode getAParametersConsumer() {
76+
result.asExpr() = this.getAParametersArg()
77+
}
78+
}
79+
80+
class X9ECParametersInstantiation extends ParametersInstantiation {
81+
X9ECParametersInstantiation() { this.(Expr).getType().getName() = "X9ECParameters" }
82+
83+
override Expr getAlgorithmArg() { result = this.getArgument(0) }
84+
}
85+
86+
class EllipticCurveStringLiteralArg extends EllipticCurveAlgorithmValueConsumer instanceof Expr {
87+
ParametersInstantiation params;
88+
89+
EllipticCurveStringLiteralArg() { this = params.getAlgorithmArg() }
90+
91+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
92+
93+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
94+
result.(EllipticCurveStringLiteralInstance).getConsumer() = this
95+
}
96+
}
97+
}
98+
399
/**
4100
* Models for the signature algorithms defined by the `org.bouncycastle.crypto.signers` package.
5101
*/
@@ -26,6 +122,40 @@ module Signers {
26122
MethodCall getAMethodCall(string name) {
27123
result.getCallee().hasQualifiedName(this.getPackage().getName(), this.getName(), name)
28124
}
125+
126+
// Overridden by subclasses to provide the message argument.
127+
Expr getMessageArg(MethodCall call) {
128+
call.getCallee().getName() = "update" and
129+
result = call.getArgument(0)
130+
}
131+
132+
// Overridden by subclasses to provide the signature argument.
133+
Expr getSignatureArg(MethodCall call) {
134+
call.getCallee().getName() = "verifySignature" and
135+
result = call.getArgument(0)
136+
}
137+
138+
// Overridden by subclasses to provide the signature output.
139+
Expr getSignatureOutput(MethodCall call) {
140+
call.getCallee().getName() = "generateSignature" and
141+
result = call
142+
}
143+
}
144+
145+
class ECDSASigner extends Signer {
146+
ECDSASigner() { this.getName().matches("ECDSA%") }
147+
148+
override Expr getMessageArg(MethodCall call) {
149+
// For ECDSA the message is passed directly to `generateSignature()`.
150+
call.getCallee().getName().matches(["generateSignature", "verifySignature"]) and
151+
result = call.getArgument(0)
152+
}
153+
154+
override Expr getSignatureArg(MethodCall call) {
155+
// For ECDSA, r and s are passed to `verifySignature()` as separate arguments.
156+
call.getCallee().getName() = "verifySignature" and
157+
result = call.getArgument([1, 2])
158+
}
29159
}
30160

31161
/**
@@ -45,7 +175,17 @@ module Signers {
45175

46176
Expr getForSigningArg() { result = this.getArgument(0) }
47177

48-
Expr getKeyArg() { result = this.getArgument(1) }
178+
Expr getKeyArg() {
179+
this.getParameterArg().getType() instanceof Params::KeyParameters and
180+
result = this.getParameterArg()
181+
}
182+
183+
// The second argument is used to provide parameters (like the key) to the signer.
184+
Expr getParameterArg() { result = this.getArgument(1) }
185+
186+
Crypto::ConsumerInputDataFlowNode getAParametersConsumer() {
187+
result.asExpr() = this.getParameterArg()
188+
}
49189

50190
// TODO: Support dataflow for the operation sub-type.
51191
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
@@ -68,36 +208,48 @@ module Signers {
68208
* verify the signature, respectively.
69209
*/
70210
private class SignerUseCall extends MethodCall {
71-
SignerUseCall() { this = any(Signer signer).getAUseCall() }
211+
Signer signer;
212+
213+
SignerUseCall() { this = signer.getAUseCall() }
72214

73215
predicate isIntermediate() { this.getCallee().getName() = "update" }
74216

75-
Expr getInput() { result = this.getArgument(0) }
217+
Expr getMessageInput() { result = signer.getMessageArg(this) }
76218

77-
Expr getOutput() { result = this }
219+
Expr getSignatureInput() { result = signer.getSignatureArg(this) }
220+
221+
Expr getSignatureOutput() { result = signer.getSignatureOutput(this) }
78222
}
79223

80224
/**
81225
* Instantiate the flow analysis module for the `Signer` class.
82226
*/
83-
private module FlowAnalysis =
227+
private module SignerFlow =
84228
NewToInitToUseFlowAnalysis<SignerNewCall, SignerInitCall, SignerUseCall>;
85229

86230
/**
87231
* A signing operation instance is a call to either `update()`, `generateSignature()`,
88232
* or `verifySignature()` on a `Signer` instance.
89233
*/
90-
class SignatureOperationInstance extends Crypto::KeyOperationInstance instanceof SignerUseCall {
234+
class SignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof SignerUseCall
235+
{
91236
SignatureOperationInstance() { not this.isIntermediate() }
92237

93238
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
94-
result = FlowAnalysis::getNewFromUse(this, _, _)
239+
result = this.getParameters().getAnAlgorithmValueConsumer()
240+
or
241+
result = SignerFlow::getNewFromUse(this, _, _)
95242
}
96243

97244
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
98-
if FlowAnalysis::hasInit(this)
99-
then result = this.getInitCall().getKeyOperationSubtype()
100-
else result = Crypto::TUnknownKeyOperationMode()
245+
// This is less expensive and more robust than resolving the subtype using
246+
// dataflow from the `forSigning` argument to `init()`.
247+
if super.getMethod().getName() = "generateSignature"
248+
then result = Crypto::TSignMode()
249+
else
250+
if super.getMethod().getName() = "verifySignature"
251+
then result = Crypto::TVerifyMode()
252+
else result = Crypto::TUnknownKeyOperationMode()
101253
}
102254

103255
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
@@ -107,18 +259,36 @@ module Signers {
107259
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() }
108260

109261
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
110-
result.asExpr() = this.getAnUpdateCall().getInput()
262+
// Inputs to signers with streaming APIs
263+
result.asExpr() = this.getAnUpdateCall().getMessageInput()
264+
or
265+
// Inputs to signers with one shot APIs
266+
result.asExpr() = super.getMessageInput()
267+
}
268+
269+
override Crypto::ConsumerInputDataFlowNode getSignatureArtifactConsumer() {
270+
result.asExpr() = super.getSignatureInput()
111271
}
112272

113273
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
114-
this.getKeyOperationSubtype() = Crypto::TSignMode() and
115-
result.asExpr() = super.getOutput()
274+
// Signature output
275+
result.asExpr() = super.getSignatureOutput()
116276
}
117277

118-
SignerInitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) }
278+
SignerInitCall getInitCall() { result = SignerFlow::getInitFromUse(this, _, _) }
119279

120280
SignerUseCall getAnUpdateCall() {
121-
result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _)
281+
result = SignerFlow::getAnIntermediateUseFromFinalUse(this, _, _)
282+
}
283+
284+
Crypto::KeyArtifactOutputInstance getKey() { result.flowsTo(this.getInitCall().getKeyArg()) }
285+
286+
Generators::KeyGenerationOperationInstance getKeyGenerationOperationInstance() {
287+
result.getKeyArtifactOutputInstance() = this.getKey()
288+
}
289+
290+
Params::ParametersInstantiation getParameters() {
291+
result = this.getKeyGenerationOperationInstance().getParameters()
122292
}
123293
}
124294
}
@@ -194,6 +364,11 @@ module Generators {
194364
// TODO: We may need to model this using the `parameters` argument passed to
195365
// the `init()` method.
196366
Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
367+
368+
// The `KeyGenerationParameters` argument used to configure the key generator.
369+
Crypto::ConsumerInputDataFlowNode getAParametersConsumer() {
370+
result.asExpr() = this.getArgument(0)
371+
}
197372
}
198373

199374
/**
@@ -216,6 +391,14 @@ module Generators {
216391
private module KeyGeneratorFlow =
217392
NewToInitToUseFlowAnalysis<KeyGeneratorNewCall, KeyGeneratorInitCall, KeyGeneratorUseCall>;
218393

394+
private module ParametersFlow =
395+
ParametersToInitFlowAnalysis<Params::ParametersInstantiation, KeyGeneratorInitCall>;
396+
397+
Params::ParametersInstantiation getParametersFromInit(KeyGeneratorInitCall init) {
398+
result = ParametersFlow::getParametersFromInit(init, _, _) and
399+
result instanceof Params::X9ECParametersInstantiation
400+
}
401+
219402
/**
220403
* A key generation operation instance is a call to `generateKey()` or
221404
* `generateKeyPair()` on a key generator defined under
@@ -224,6 +407,10 @@ module Generators {
224407
class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall
225408
{
226409
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
410+
// The algorithm value consumer flows through a parameters argument to `init()`
411+
result = this.getParameters().getAnAlgorithmValueConsumer()
412+
or
413+
// The algorithm is implicit in the key generator type
227414
result = KeyGeneratorFlow::getNewFromUse(this, _, _)
228415
}
229416

@@ -240,17 +427,12 @@ module Generators {
240427
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
241428
result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer()
242429
}
243-
}
244-
}
245430

246-
/**
247-
* Models for cryptographic parameters defined by the `org.bouncycastle.crypto.params` package.
248-
*/
249-
module Parameters {
250-
class KeyGenerationParameters extends RefType {
251-
KeyGenerationParameters() {
252-
this.getPackage().getName() = "org.bouncycastle.crypto.params" and
253-
this.getName().matches("%KeyGenerationParameters")
431+
Params::ParametersInstantiation getParameters() {
432+
exists(KeyGeneratorInitCall init |
433+
init = KeyGeneratorFlow::getInitFromUse(this, _, _) and
434+
result = ParametersFlow::getParametersFromInit(init, _, _)
435+
)
254436
}
255437
}
256438
}

0 commit comments

Comments
 (0)