Skip to content

Commit b3edef2

Browse files
committed
Added support for BouncyCastle key generation algorithms
This commit adds the `KeyGenerationOperationInstance` and `KeyGenerationAlgorithmInstance` types to the BouncyCastle model. It also adds data flow support from key pairs to the corresponding public and private components.
1 parent 03c2dfa commit b3edef2

File tree

5 files changed

+311
-59
lines changed

5 files changed

+311
-59
lines changed

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

Lines changed: 142 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ module Signers {
3232
* BouncyCastle algorithms are instantiated by calling the constructor of the
3333
* corresponding class.
3434
*/
35-
class NewCall = SignatureAlgorithmInstance;
35+
private class NewCall = SignatureAlgorithmInstance;
3636

3737
/**
3838
* The type is instantiated by a constructor call and initialized by a call to
3939
* `init()` which takes two arguments. The first argument is a flag indicating
4040
* whether the operation is signing data or verifying a signature, and the
4141
* second is the key to use.
4242
*/
43-
class InitCall extends MethodCall {
43+
private class InitCall extends MethodCall {
4444
InitCall() { this = any(Signer signer).getAnInitCall() }
4545

4646
Expr getForSigningArg() { result = this.getArgument(0) }
@@ -67,7 +67,7 @@ module Signers {
6767
* `generateSignature()` or `verifySignature()` methods are used to produce or
6868
* verify the signature, respectively.
6969
*/
70-
class UseCall extends MethodCall {
70+
private class UseCall extends MethodCall {
7171
UseCall() { this = any(Signer signer).getAUseCall() }
7272

7373
predicate isIntermediate() { this.getCallee().getName() = "update" }
@@ -80,7 +80,7 @@ module Signers {
8080
/**
8181
* Instantiate the flow analysis module for the `Signer` class.
8282
*/
83-
module FlowAnalysis = NewToInitToUseFlowAnalysis<NewCall, InitCall, UseCall>;
83+
private module FlowAnalysis = NewToInitToUseFlowAnalysis<NewCall, InitCall, UseCall>;
8484

8585
/**
8686
* A signing operation instance is a call to either `update()`, `generateSignature()`,
@@ -90,7 +90,7 @@ module Signers {
9090
SignatureOperationInstance() { not this.isIntermediate() }
9191

9292
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
93-
result = FlowAnalysis::getInstantiationFromUse(this, _, _)
93+
result = FlowAnalysis::getNewFromUse(this, _, _)
9494
}
9595

9696
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
@@ -111,16 +111,150 @@ module Signers {
111111

112112
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
113113
this.getKeyOperationSubtype() = Crypto::TSignMode() and
114-
not super.isIntermediate() and
115114
result.asExpr() = super.getOutput()
116115
}
117116

118117
InitCall getInitCall() { result = FlowAnalysis::getInitFromUse(this, _, _) }
119118

120119
UseCall getAnUpdateCall() {
121-
super.isIntermediate() and result = this
122-
or
123120
result = FlowAnalysis::getAnIntermediateUseFromFinalUse(this, _, _)
124121
}
125122
}
126123
}
124+
125+
/**
126+
* Models for the key generation algorithms defined by the `org.bouncycastle.crypto.generators` package.
127+
*/
128+
module Generators {
129+
import Language
130+
import BouncyCastle.FlowAnalysis
131+
import BouncyCastle.AlgorithmInstances
132+
133+
/**
134+
* A model of the `KeyGenerator` and `KeyPairGenerator` classes in Bouncy Castle.
135+
*/
136+
class KeyGenerator extends RefType {
137+
Crypto::KeyArtifactType type;
138+
139+
KeyGenerator() {
140+
this.getPackage().getName() = "org.bouncycastle.crypto.generators" and
141+
(
142+
this.getName().matches("%KeyGenerator") and type instanceof Crypto::TSymmetricKeyType
143+
or
144+
this.getName().matches("%KeyPairGenerator") and type instanceof Crypto::TAsymmetricKeyType
145+
)
146+
}
147+
148+
MethodCall getAnInitCall() { result = this.getAMethodCall("init") }
149+
150+
MethodCall getAUseCall() { result = this.getAMethodCall(["generateKey", "generateKeyPair"]) }
151+
152+
MethodCall getAMethodCall(string name) {
153+
result
154+
.getCallee()
155+
.hasQualifiedName("org.bouncycastle.crypto.generators", this.getName(), name)
156+
}
157+
158+
Crypto::KeyArtifactType getKeyType() { result = type }
159+
160+
string getRawAlgorithmName() {
161+
this.getKeyType() = Crypto::TSymmetricKeyType() and
162+
result = this.getName().splitAt("KeyGenerator", 0)
163+
or
164+
this.getKeyType() = Crypto::TAsymmetricKeyType() and
165+
result = this.getName().splitAt("KeyPairGenerator", 0)
166+
}
167+
}
168+
169+
/**
170+
* This type is used to model data flow from a key pair to the private and
171+
* public components of the key pair.
172+
*/
173+
class KeyPair extends RefType {
174+
KeyPair() {
175+
this.getPackage().getName() = "org.bouncycastle.crypto" and
176+
this.getName() = "%KeyPair" // `AsymmetricCipherKeyPair` or `EphemeralKeyPair`
177+
}
178+
179+
MethodCall getPublicKeyCall() { result = this.getAMethodCall("getPublic") }
180+
181+
MethodCall getPrivateKeyCall() { result = this.getAMethodCall("getPrivate") }
182+
183+
MethodCall getAMethodCall(string name) {
184+
result.getCallee().hasQualifiedName("org.bouncycastle.crypto", this.getName(), name)
185+
}
186+
}
187+
188+
/**
189+
* BouncyCastle algorithms are instantiated by calling the constructor of the
190+
* corresponding class.
191+
*/
192+
private class KeyGeneratorNewCall = KeyGenerationAlgorithmInstance;
193+
194+
/**
195+
* The type is instantiated by a constructor call and initialized by a call to
196+
* `init()` which takes a single `KeyGenerationParameters` argument.
197+
*/
198+
private class KeyGeneratorInitCall extends MethodCall {
199+
KeyGenerator gen;
200+
201+
KeyGeneratorInitCall() { this = gen.getAnInitCall() }
202+
203+
// TODO: We may need to model this using the `parameters` argument passed to
204+
// the `init()` method.
205+
Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
206+
}
207+
208+
/**
209+
* The `generateKey()` and `generateKeyPair()` methods are used to generate
210+
* the resulting key, depending on the type of the generator.
211+
*/
212+
private class KeyGeneratorUseCall extends MethodCall {
213+
KeyGenerator gen;
214+
215+
KeyGeneratorUseCall() { this = gen.getAUseCall() }
216+
217+
// Since key generators don't have `update()` methods, this is always false.
218+
predicate isIntermediate() { none() }
219+
220+
Crypto::KeyArtifactType getKeyType() { result = gen.getKeyType() }
221+
222+
Expr getOutput() { result = this }
223+
}
224+
225+
private module KeyGeneratorFlow =
226+
NewToInitToUseFlowAnalysis<KeyGeneratorNewCall, KeyGeneratorInitCall, KeyGeneratorUseCall>;
227+
228+
/**
229+
* A key generation operation instance is a call to `generateKey()` or
230+
* `generateKeyPair()` on a key generator defined under
231+
* `org.bouncycastle.crypto.generators`.
232+
*/
233+
class KeyGenerationOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGeneratorUseCall
234+
{
235+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
236+
result = KeyGeneratorFlow::getNewFromUse(this, _, _)
237+
}
238+
239+
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
240+
result.asExpr() = super.getOutput()
241+
}
242+
243+
override Crypto::KeyArtifactType getOutputKeyType() { result = super.getKeyType() }
244+
245+
override string getKeySizeFixed() {
246+
result = KeyGeneratorFlow::getNewFromUse(this, _, _).getKeySizeFixed()
247+
}
248+
249+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
250+
result = KeyGeneratorFlow::getInitFromUse(this, _, _).getKeySizeConsumer()
251+
}
252+
}
253+
254+
class KeyGenerationParameters extends RefType {
255+
KeyGenerationParameters() {
256+
this.getPackage().getName() = "org.bouncycastle.crypto.generators" and
257+
this.getName().matches("%KeyGenerationParameters")
258+
}
259+
}
260+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import java
22
import experimental.quantum.Language
33
import AlgorithmInstances.SignatureAlgorithmInstances
4+
import AlgorithmInstances.KeyGenerationAlgorithmInstance
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import java
2+
import experimental.quantum.Language
3+
4+
/**
5+
* Key generation algorithms are implicitly defined by the constructor.
6+
*/
7+
abstract private class KeyGenerationAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
8+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this }
9+
10+
override Crypto::ConsumerInputDataFlowNode getInputNode() { none() }
11+
}
12+
13+
class KeyGenerationAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance,
14+
KeyGenerationAlgorithmValueConsumer instanceof ClassInstanceExpr
15+
{
16+
KeyGenerationAlgorithmInstance() {
17+
super.getConstructedType() instanceof Generators::KeyGenerator
18+
}
19+
20+
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() }
21+
22+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
23+
24+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
25+
nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result)
26+
}
27+
28+
override string getKeySizeFixed() {
29+
nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _)
30+
}
31+
32+
override string getRawAlgorithmName() {
33+
result = super.getConstructedType().(Generators::KeyGenerator).getRawAlgorithmName()
34+
}
35+
36+
// This is overridden if a specific generator type has a key size consumer.
37+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
38+
}
39+
40+
class CramerShoupKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance {
41+
CramerShoupKeyGenerationAlgorithmInstance() { super.getRawAlgorithmName() = "CramerShoup" }
42+
43+
override string getKeySizeFixed() { none() }
44+
45+
// TODO: Model flow from the `CramerShoupParametersGenerator::init` method
46+
// (which takes a size of the prime order group in bits), via the
47+
// `CramerShoupParameters` object, to the key-pair generator.
48+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
49+
50+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
51+
result = Crypto::KeyOpAlg::TAsymmetricCipher(Crypto::KeyOpAlg::OtherAsymmetricCipherType())
52+
}
53+
}
54+
55+
class DESedeKeyGenerationAlgorithmInstance extends KeyGenerationAlgorithmInstance {
56+
DESedeKeyGenerationAlgorithmInstance() { super.getRawAlgorithmName() = "DESede" }
57+
58+
// Key size is 112 or 168 bits.
59+
override string getKeySizeFixed() { none() }
60+
61+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
62+
63+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
64+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::TripleDES())
65+
}
66+
}
67+
68+
private predicate nameToKeySizeAndAlgorithmMapping(
69+
string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm
70+
) {
71+
// DES
72+
name = "DES" and
73+
keySize = "56" and
74+
algorithm = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
75+
or
76+
// Ed25519, Ed25519ph, and Ed25519ctx
77+
name = "Ed25519" and
78+
keySize = "256" and
79+
algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519())
80+
or
81+
// Ed448 and Ed448ph
82+
name = "Ed448" and
83+
keySize = "448" and
84+
algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448())
85+
}

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

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,47 @@ import experimental.quantum.Language
44
/**
55
* Signature algorithms are implicitly defined by the constructor.
66
*/
7-
private abstract class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
8-
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
9-
result = this
10-
}
7+
abstract private class SignatureAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
8+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this }
119

12-
override Crypto::ConsumerInputDataFlowNode getInputNode() {
13-
none()
14-
}
10+
override Crypto::ConsumerInputDataFlowNode getInputNode() { none() }
1511
}
1612

17-
class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr {
18-
SignatureAlgorithmInstance() {
19-
super.getConstructedType().getPackage().getName() = "org.bouncycastle.crypto.signers" and
20-
super.getConstructedType().getName().matches("%Signer")
21-
22-
}
13+
class SignatureAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance,
14+
SignatureAlgorithmValueConsumer instanceof ClassInstanceExpr
15+
{
16+
SignatureAlgorithmInstance() { super.getConstructedType() instanceof Signers::Signer }
2317

24-
// TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx.
18+
// TODO: Could potentially be used to model signature modes like Ed25519ph and Ed25519ctx.
2519
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() }
2620

2721
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
28-
22+
2923
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
3024

3125
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
32-
nameToTypeAndKeySizeMapping(this.getRawAlgorithmName(), _, result)
26+
nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), _, result)
3327
}
3428

35-
override string getRawAlgorithmName() {
29+
override string getRawAlgorithmName() {
3630
result = super.getConstructedType().getName().splitAt("Signer", 0)
3731
}
3832

39-
override string getKeySizeFixed() {
40-
nameToTypeAndKeySizeMapping(this.getRawAlgorithmName(), result, _)
33+
override string getKeySizeFixed() {
34+
nameToKeySizeAndAlgorithmMapping(this.getRawAlgorithmName(), result, _)
4135
}
4236
}
4337

44-
predicate nameToTypeAndKeySizeMapping(string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm) {
45-
name = "Ed25519" and keySize = "256" and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519())
38+
private predicate nameToKeySizeAndAlgorithmMapping(
39+
string name, string keySize, Crypto::KeyOpAlg::Algorithm algorithm
40+
) {
41+
// Ed25519, Ed25519ph, and Ed25519ctx
42+
name = "Ed25519%" and
43+
keySize = "256" and
44+
algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed25519())
4645
or
47-
name = "Ed448" and keySize = "448" and algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448())
46+
// Ed448 and Ed448ph
47+
name = "Ed448%" and
48+
keySize = "448" and
49+
algorithm = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::Ed448())
4850
}
49-
50-
51-

0 commit comments

Comments
 (0)