Skip to content

Commit 9238b12

Browse files
committed
Added support for the system CSPRNG
1 parent c7caf97 commit 9238b12

File tree

2 files changed

+141
-17
lines changed

2 files changed

+141
-17
lines changed

csharp/ql/lib/experimental/quantum/Language.qll

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
private import csharp as Language
2+
private import semmle.code.csharp.dataflow.DataFlow
23
private import codeql.quantum.experimental.Model
34

45
private class UnknownLocation extends Language::Location {
@@ -41,28 +42,142 @@ module CryptoInput implements InputSig<Language::Location> {
4142
// Instantiate the `CryptographyBase` module
4243
module Crypto = CryptographyBase<Language::Location, CryptoInput>;
4344

44-
module ArtifactFlowConfig implements Language::DataFlow::ConfigSig {
45-
predicate isSource(Language::DataFlow::Node source) {
45+
/**
46+
* An additional flow step in generic data-flow configurations.
47+
* Where a step is an edge between nodes `n1` and `n2`,
48+
* `this` = `n1` and `getOutput()` = `n2`.
49+
*
50+
* FOR INTERNAL MODELING USE ONLY.
51+
*/
52+
abstract class AdditionalFlowInputStep extends DataFlow::Node {
53+
abstract DataFlow::Node getOutput();
54+
55+
final DataFlow::Node getInput() { result = this }
56+
}
57+
58+
/**
59+
* Generic data source to node input configuration
60+
*/
61+
module GenericDataSourceFlowConfig implements DataFlow::ConfigSig {
62+
predicate isSource(DataFlow::Node source) {
63+
source = any(Crypto::GenericSourceInstance i).getOutputNode()
64+
}
65+
66+
predicate isSink(DataFlow::Node sink) {
67+
sink = any(Crypto::FlowAwareElement other).getInputNode()
68+
}
69+
70+
predicate isBarrierOut(DataFlow::Node node) {
71+
node = any(Crypto::FlowAwareElement element).getInputNode()
72+
}
73+
74+
predicate isBarrierIn(DataFlow::Node node) {
75+
node = any(Crypto::FlowAwareElement element).getOutputNode()
76+
}
77+
78+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
79+
node1.(AdditionalFlowInputStep).getOutput() = node2
80+
}
81+
}
82+
83+
module ArtifactFlowConfig implements DataFlow::ConfigSig {
84+
predicate isSource(DataFlow::Node source) {
4685
source = any(Crypto::ArtifactInstance artifact).getOutputNode()
4786
}
4887

49-
predicate isSink(Language::DataFlow::Node sink) {
88+
predicate isSink(DataFlow::Node sink) {
5089
sink = any(Crypto::FlowAwareElement other).getInputNode()
5190
}
5291

53-
predicate isBarrierOut(Language::DataFlow::Node node) {
92+
predicate isBarrierOut(DataFlow::Node node) {
5493
node = any(Crypto::FlowAwareElement element).getInputNode()
5594
}
5695

57-
predicate isBarrierIn(Language::DataFlow::Node node) {
96+
predicate isBarrierIn(DataFlow::Node node) {
5897
node = any(Crypto::FlowAwareElement element).getOutputNode()
5998
}
6099

61-
predicate isAdditionalFlowStep(Language::DataFlow::Node node1, Language::DataFlow::Node node2) {
62-
none()
100+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
101+
node1.(AdditionalFlowInputStep).getOutput() = node2
102+
}
103+
}
104+
105+
module GenericDataSourceFlow = TaintTracking::Global<GenericDataSourceFlowConfig>;
106+
107+
module ArtifactFlow = DataFlow::Global<ArtifactFlowConfig>;
108+
109+
/**
110+
* A method access that returns random data or writes random data to an argument.
111+
*/
112+
abstract class RandomnessSource extends MethodCall {
113+
/**
114+
* Gets the expression representing the output of this source.
115+
*/
116+
abstract Expr getOutput();
117+
118+
/**
119+
* Gets the type of the source of randomness used by this call.
120+
*/
121+
Type getGenerator() { result = this.getQualifier().getType() }
122+
}
123+
124+
/**
125+
* A call to `System.Security.Cryptography.RandomNumberGenerator.GetBytes`.
126+
*/
127+
class SecureRandomnessSource extends RandomnessSource {
128+
SecureRandomnessSource() {
129+
this.getTarget()
130+
.hasFullyQualifiedName("System.Security.Cryptography", "RandomNumberGenerator", "GetBytes")
131+
}
132+
133+
override Expr getOutput() { result = this.getArgument(0) }
134+
}
135+
136+
/**
137+
* A call to `System.Random.NextBytes`.
138+
*/
139+
class InsecureRandomnessSource extends RandomnessSource {
140+
InsecureRandomnessSource() {
141+
this.getTarget().hasFullyQualifiedName("System", "Random", "NextBytes")
142+
}
143+
144+
override Expr getOutput() { result = this.getArgument(0) }
145+
}
146+
147+
/**
148+
* An instance of random number generation, modelled as the expression
149+
* tied to an output node (i.e., the RNG output)
150+
*/
151+
abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance {
152+
override DataFlow::Node getOutputNode() { result.asExpr() = this }
153+
}
154+
155+
/**
156+
* An output instance from the system cryptographically secure RNG.
157+
*/
158+
class SecureRandomnessInstance extends RandomnessInstance {
159+
RandomnessSource source;
160+
161+
SecureRandomnessInstance() {
162+
source instanceof SecureRandomnessSource and
163+
this = source.getOutput()
63164
}
165+
166+
override string getGeneratorName() { result = source.getGenerator().getName() }
64167
}
65168

66-
module ArtifactFlow = Language::DataFlow::Global<ArtifactFlowConfig>;
169+
/**
170+
* An output instance from an insecure RNG.
171+
*/
172+
class InsecureRandomnessInstance extends RandomnessInstance {
173+
RandomnessSource source;
174+
175+
InsecureRandomnessInstance() {
176+
not source instanceof SecureRandomnessSource and
177+
this = source.getOutput()
178+
}
179+
180+
override string getGeneratorName() { result = source.getGenerator().getName() }
181+
}
67182

68183
import dotnet

csharp/ql/lib/experimental/quantum/dotnet/FlowAnalysis.qll

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
private import csharp
22
private import semmle.code.csharp.dataflow.DataFlow
3+
private import experimental.quantum.Language
34
private import OperationInstances
45
private import AlgorithmValueConsumers
56
private import Cryptography
@@ -51,15 +52,6 @@ module CreationToUseFlow<CreationCallSig Creation, UseCallSig Use> {
5152
sink.getNode().asExpr() = use.(QualifiableExpr).getQualifier() and
5253
CreationToUseFlow::flowPath(source, sink)
5354
}
54-
55-
// TODO: Remove this.
56-
Expr flowsTo(Expr expr) {
57-
exists(CreationToUseFlow::PathNode source, CreationToUseFlow::PathNode sink |
58-
source.getNode().asExpr() = expr and
59-
sink.getNode().asExpr() = result and
60-
CreationToUseFlow::flowPath(source, sink)
61-
)
62-
}
6355
}
6456

6557
/**
@@ -269,3 +261,20 @@ module StreamFlow {
269261
StreamFlow::flowPath(source, sink)
270262
}
271263
}
264+
265+
/**
266+
* An additional flow step across property assignments used to track flow from
267+
* output artifacts to consumers.
268+
*
269+
* TODO: Figure out why this is needed.
270+
*/
271+
class PropertyWriteFlowStep extends AdditionalFlowInputStep {
272+
Assignment assignment;
273+
274+
PropertyWriteFlowStep() {
275+
this.asExpr() = assignment.getRValue() and
276+
assignment.getLValue() instanceof PropertyWrite
277+
}
278+
279+
override DataFlow::Node getOutput() { result.asExpr() = assignment.getLValue() }
280+
}

0 commit comments

Comments
 (0)