Skip to content

Commit b1bbd97

Browse files
committed
Added initial support for C# block ciphers
1 parent 01b98fc commit b1bbd97

File tree

6 files changed

+440
-32
lines changed

6 files changed

+440
-32
lines changed

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

@@ -40,3 +41,67 @@ class HashAlgorithmNameInstance extends Crypto::HashAlgorithmInstance instanceof
4041

4142
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
4243
}
44+
45+
class SymmetricAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof SymmetricAlgorithmCreation
46+
{
47+
override string getRawAlgorithmName() { result = super.getSymmetricAlgorithm().getName() }
48+
49+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
50+
if exists(symmetricAlgorithmNameToType(this.getRawAlgorithmName()))
51+
then result = symmetricAlgorithmNameToType(this.getRawAlgorithmName())
52+
else result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::OtherSymmetricCipherType())
53+
}
54+
55+
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() }
56+
57+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
58+
59+
override int getKeySizeFixed() { none() }
60+
61+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
62+
}
63+
64+
/**
65+
* A padding mode literal, such as `PaddingMode.PKCS7`.
66+
*/
67+
class PaddingModeLiteralInstance extends Crypto::PaddingAlgorithmInstance instanceof MemberConstantAccess
68+
{
69+
Crypto::AlgorithmValueConsumer consumer;
70+
71+
PaddingModeLiteralInstance() {
72+
this = any(PaddingMode mode).getAnAccess() and
73+
consumer = PaddingModeLiteralFlow::getConsumer(this, _, _)
74+
}
75+
76+
override string getRawPaddingAlgorithmName() { result = super.getTarget().getName() }
77+
78+
override Crypto::TPaddingType getPaddingType() {
79+
if exists(paddingNameToType(this.getRawPaddingAlgorithmName()))
80+
then result = paddingNameToType(this.getRawPaddingAlgorithmName())
81+
else result = Crypto::OtherPadding()
82+
}
83+
84+
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
85+
}
86+
87+
private Crypto::KeyOpAlg::Algorithm symmetricAlgorithmNameToType(string algorithmName) {
88+
algorithmName = "Aes" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
89+
or
90+
algorithmName = "DES" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
91+
or
92+
algorithmName = "RC2" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::RC2())
93+
or
94+
algorithmName = "Rijndael" and
95+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
96+
or
97+
algorithmName = "TripleDES" and
98+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
99+
}
100+
101+
private Crypto::TPaddingType paddingNameToType(string paddingName) {
102+
paddingName = "ANSIX923" and result = Crypto::ANSI_X9_23()
103+
or
104+
paddingName = "None" and result = Crypto::NoPadding()
105+
or
106+
paddingName = "PKCS7" and result = Crypto::PKCS7()
107+
}

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

0 commit comments

Comments
 (0)