Skip to content

Commit 2642e32

Browse files
committed
Merge branch 'tob/quantum-csharp' of github.com:trailofbits/codeql into tob/quantum-csharp
2 parents 791c260 + b1bbd97 commit 2642e32

6 files changed

Lines changed: 442 additions & 32 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,4 @@ module ArtifactFlowConfig implements Language::DataFlow::ConfigSig {
6565

6666
module ArtifactFlow = Language::DataFlow::Global<ArtifactFlowConfig>;
6767

68-
import dotnet
68+
import dotnet
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import dotnet.AlgorithmInstances
21
import dotnet.AlgorithmValueConsumers
3-
import dotnet.FlowAnalysis
4-
import dotnet.Cryptography
2+
import dotnet.AlgorithmInstances
3+
import dotnet.OperationInstances

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import csharp
22
private import experimental.quantum.Language
33
private import AlgorithmValueConsumers
4+
private import OperationInstances
45
private import Cryptography
56
private import FlowAnalysis
67

@@ -64,3 +65,67 @@ class HashAlgorithmNameInstance extends Crypto::HashAlgorithmInstance instanceof
6465

6566
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
6667
}
68+
69+
class SymmetricAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof SymmetricAlgorithmCreation
70+
{
71+
override string getRawAlgorithmName() { result = super.getSymmetricAlgorithm().getName() }
72+
73+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
74+
if exists(symmetricAlgorithmNameToType(this.getRawAlgorithmName()))
75+
then result = symmetricAlgorithmNameToType(this.getRawAlgorithmName())
76+
else result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::OtherSymmetricCipherType())
77+
}
78+
79+
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() }
80+
81+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
82+
83+
override int getKeySizeFixed() { none() }
84+
85+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
86+
}
87+
88+
/**
89+
* A padding mode literal, such as `PaddingMode.PKCS7`.
90+
*/
91+
class PaddingModeLiteralInstance extends Crypto::PaddingAlgorithmInstance instanceof MemberConstantAccess
92+
{
93+
Crypto::AlgorithmValueConsumer consumer;
94+
95+
PaddingModeLiteralInstance() {
96+
this = any(PaddingMode mode).getAnAccess() and
97+
consumer = PaddingModeLiteralFlow::getConsumer(this, _, _)
98+
}
99+
100+
override string getRawPaddingAlgorithmName() { result = super.getTarget().getName() }
101+
102+
override Crypto::TPaddingType getPaddingType() {
103+
if exists(paddingNameToType(this.getRawPaddingAlgorithmName()))
104+
then result = paddingNameToType(this.getRawPaddingAlgorithmName())
105+
else result = Crypto::OtherPadding()
106+
}
107+
108+
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
109+
}
110+
111+
private Crypto::KeyOpAlg::Algorithm symmetricAlgorithmNameToType(string algorithmName) {
112+
algorithmName = "Aes" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
113+
or
114+
algorithmName = "DES" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
115+
or
116+
algorithmName = "RC2" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::RC2())
117+
or
118+
algorithmName = "Rijndael" and
119+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
120+
or
121+
algorithmName = "TripleDES" and
122+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
123+
}
124+
125+
private Crypto::TPaddingType paddingNameToType(string paddingName) {
126+
paddingName = "ANSIX923" and result = Crypto::ANSI_X9_23()
127+
or
128+
paddingName = "None" and result = Crypto::NoPadding()
129+
or
130+
paddingName = "PKCS7" and result = Crypto::PKCS7()
131+
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import csharp
22
private import experimental.quantum.Language
33
private import AlgorithmInstances
4+
private import OperationInstances
45
private import Cryptography
56

67
class ECDsaAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer {
@@ -26,3 +27,16 @@ class HashAlgorithmNameConsumer extends Crypto::AlgorithmValueConsumer {
2627
exists(HashAlgorithmNameInstance l | l.getConsumer() = this and result = l)
2728
}
2829
}
30+
31+
/**
32+
* A write access to the `Padding` property of a `SymmetricAlgorithm` instance.
33+
*/
34+
class PaddingPropertyWrite extends Crypto::AlgorithmValueConsumer instanceof SymmetricAlgorithmUse {
35+
PaddingPropertyWrite() { super.isPaddingConsumer() }
36+
37+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
38+
39+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
40+
result.(PaddingModeLiteralInstance).getConsumer() = this
41+
}
42+
}

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

Lines changed: 161 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,16 @@
11
private import csharp
2-
private import Cryptography
2+
private import semmle.code.csharp.dataflow.DataFlow
3+
private import OperationInstances
34
private import AlgorithmValueConsumers
4-
5-
/**
6-
* Flow from a known ECDsa property access to a `ECDsa.Create(sink)` call.
7-
*/
8-
module SigningNamedCurveToSignatureCreateFlowConfig implements DataFlow::ConfigSig {
9-
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SigningNamedCurvePropertyAccess }
10-
11-
predicate isSink(DataFlow::Node sink) {
12-
exists(ECDsaAlgorithmValueConsumer consumer | sink = consumer.getInputNode())
13-
}
14-
}
15-
16-
module SigningNamedCurveToSignatureCreateFlow =
17-
DataFlow::Global<SigningNamedCurveToSignatureCreateFlowConfig>;
18-
19-
module HashAlgorithmNameToUseConfig implements DataFlow::ConfigSig {
20-
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HashAlgorithmName }
21-
22-
predicate isSink(DataFlow::Node sink) {
23-
exists(HashAlgorithmNameConsumer consumer | sink = consumer.getInputNode())
24-
}
25-
}
26-
27-
module HashAlgorithmNameToUse = DataFlow::Global<HashAlgorithmNameToUseConfig>;
5+
private import Cryptography
286

297
signature class CreationCallSig instanceof Call;
308

319
signature class UseCallSig instanceof QualifiableExpr {
3210
predicate isIntermediate();
3311
}
3412

35-
module SigningCreateToUseFlow = CreationToUseFlow<SigningCreateCall, SignerUse>;
3613

37-
module HashCreateToUseFlow = CreationToUseFlow<HashAlgorithmCreateCall, HashUse>;
3814

3915
module CreationToUseFlow<CreationCallSig Creation, UseCallSig Use> {
4016
private module CreationToUseConfig implements DataFlow::ConfigSig {
@@ -87,3 +63,161 @@ module CreationToUseFlow<CreationCallSig Creation, UseCallSig Use> {
8763
)
8864
}
8965
}
66+
67+
/**
68+
* Flow from a known ECDsa property access to a `ECDsa.Create(sink)` call.
69+
*/
70+
module SigningNamedCurveToSignatureCreateFlowConfig implements DataFlow::ConfigSig {
71+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SigningNamedCurvePropertyAccess }
72+
73+
predicate isSink(DataFlow::Node sink) {
74+
exists(ECDsaAlgorithmValueConsumer consumer | sink = consumer.getInputNode())
75+
}
76+
}
77+
78+
module SigningNamedCurveToSignatureCreateFlow =
79+
DataFlow::Global<SigningNamedCurveToSignatureCreateFlowConfig>;
80+
81+
module HashAlgorithmNameToUseConfig implements DataFlow::ConfigSig {
82+
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HashAlgorithmName }
83+
84+
predicate isSink(DataFlow::Node sink) {
85+
exists(HashAlgorithmNameConsumer consumer | sink = consumer.getInputNode())
86+
}
87+
}
88+
89+
module HashAlgorithmNameToUse = DataFlow::Global<HashAlgorithmNameToUseConfig>;
90+
91+
module SigningCreateToUseFlow = CreationToUseFlow<SigningCreateCall, SignerUse>;
92+
93+
module HashCreateToUseFlow = CreationToUseFlow<HashAlgorithmCreateCall, HashUse>;
94+
95+
/**
96+
* A flow analysis module that tracks the flow from a `CryptoStreamMode.READ` or
97+
* `CryptoStreamMode.WRITE` access to the corresponding `CryptoStream` object
98+
* creation.
99+
*/
100+
module CryptoStreamModeFlow {
101+
private module CryptoStreamModeConfig implements DataFlow::ConfigSig {
102+
predicate isSource(DataFlow::Node source) {
103+
source.asExpr() = any(CryptoStreamMode mode).getAnAccess()
104+
}
105+
106+
predicate isSink(DataFlow::Node sink) {
107+
sink.asExpr() = any(CryptoStreamCreation creation).getModeArg()
108+
}
109+
}
110+
111+
private module CryptoStreamModeFlow = DataFlow::Global<CryptoStreamModeConfig>;
112+
113+
CryptoStreamMode getModeFromCreation(CryptoStreamCreation creation) {
114+
exists(CryptoStreamModeFlow::PathNode source, CryptoStreamModeFlow::PathNode sink |
115+
source.getNode().asExpr() = result.getAnAccess() and
116+
sink.getNode().asExpr() = creation.getAnArgument() and
117+
CryptoStreamModeFlow::flowPath(source, sink)
118+
)
119+
}
120+
}
121+
122+
/**
123+
* A flow analysis module that tracks data flow from a `ICryptoTransform`
124+
* creation (e.g. `Aes.CreateEncryptor()`) to the transform argument of a
125+
* `CryptoStream` object creation.
126+
*/
127+
module CryptoTransformFlow {
128+
private module CryptoTransformConfig implements DataFlow::ConfigSig {
129+
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CryptoTransformCreation }
130+
131+
predicate isSink(DataFlow::Node sink) {
132+
sink.asExpr() = any(ObjectCreation creation).getAnArgument()
133+
}
134+
}
135+
136+
private module CryptoTransformFlow = DataFlow::Global<CryptoTransformConfig>;
137+
138+
CryptoTransformCreation getCreationFromUse(ObjectCreation creation) {
139+
exists(CryptoTransformFlow::PathNode source, CryptoTransformFlow::PathNode sink |
140+
source.getNode().asExpr() = result and
141+
sink.getNode().asExpr() = creation.getAnArgument() and
142+
CryptoTransformFlow::flowPath(source, sink)
143+
)
144+
}
145+
}
146+
147+
/**
148+
* A flow analysis module that tracks the flow from a `PaddingMode` member
149+
* access (e.g. `PaddingMode.PKCS7`) to a `Padding` property write on a
150+
* `SymmetricAlgorithm` instance.
151+
*
152+
* Example:
153+
* ```
154+
* Aes aes = Aes.Create();
155+
* aes.Padding = PaddingMode.PKCS7;
156+
* ...
157+
* ```
158+
*/
159+
module PaddingModeLiteralFlow {
160+
private module PaddingModeLiteralConfig implements DataFlow::ConfigSig {
161+
predicate isSource(DataFlow::Node source) {
162+
source.asExpr() = any(PaddingMode mode).getAnAccess()
163+
}
164+
165+
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof PaddingPropertyWrite }
166+
167+
// TODO: Figure out why this is needed.
168+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
169+
exists(Assignment assign |
170+
node1.asExpr() = assign.getRValue() and
171+
node2.asExpr() = assign.getLValue()
172+
)
173+
}
174+
}
175+
176+
private module PaddingModeLiteralFlow = DataFlow::Global<PaddingModeLiteralConfig>;
177+
178+
SymmetricAlgorithmUse getConsumer(
179+
Expr mode, PaddingModeLiteralFlow::PathNode source, PaddingModeLiteralFlow::PathNode sink
180+
) {
181+
source.getNode().asExpr() = mode and
182+
sink.getNode().asExpr() = result and
183+
PaddingModeLiteralFlow::flowPath(source, sink)
184+
}
185+
}
186+
187+
/**
188+
* A flow analysis module that tracks the flow from a `MemoryStream` object
189+
* creation to the `stream` argument passed to a `CryptoStream` constructor
190+
* call.
191+
*
192+
* TODO: This should probably be made generic over multiple stream types.
193+
*/
194+
module MemoryStreamFlow {
195+
private class MemoryStreamCreation extends ObjectCreation {
196+
MemoryStreamCreation() {
197+
this.getObjectType().hasFullyQualifiedName("System.IO", "MemoryStream")
198+
}
199+
200+
Expr getBufferArg() { result = this.getArgument(0) }
201+
}
202+
203+
// (Note that we cannot use `CreationToUseFlow` here, because the use is not a
204+
// `QualifiableExpr`.)
205+
private module MemoryStreamConfig implements DataFlow::ConfigSig {
206+
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof MemoryStreamCreation }
207+
208+
predicate isSink(DataFlow::Node sink) {
209+
exists(CryptoStreamCreation creation | sink.asExpr() = creation.getStreamArg())
210+
}
211+
}
212+
213+
private module MemoryStreamFlow = DataFlow::Global<MemoryStreamConfig>;
214+
215+
MemoryStreamCreation getCreationFromUse(
216+
CryptoStreamCreation creation, MemoryStreamFlow::PathNode source,
217+
MemoryStreamFlow::PathNode sink
218+
) {
219+
source.getNode().asExpr() = result and
220+
sink.getNode().asExpr() = creation.getStreamArg() and
221+
MemoryStreamFlow::flowPath(source, sink)
222+
}
223+
}

0 commit comments

Comments
 (0)