Skip to content

Commit 298d226

Browse files
committed
Added support for bare symmetric algorithms
1 parent 601d5d1 commit 298d226

File tree

10 files changed

+278
-44
lines changed

10 files changed

+278
-44
lines changed

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

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,47 +46,82 @@ class HashAlgorithmNameInstance extends Crypto::HashAlgorithmInstance instanceof
4646
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
4747
}
4848

49-
class SymmetricAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof SymmetricAlgorithmCreation
49+
/**
50+
* A call to an encryption, decryption, or transform creation API (e.g.
51+
* `EncryptCbc` or `CreateEncryptor`) on a `SymmetricAlgorithm` instance.
52+
*/
53+
class SymmetricAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof SymmetricAlgorithmUse
5054
{
51-
SymmetricAlgorithmConsumer consumer;
52-
53-
SymmetricAlgorithmInstance() { consumer = SymmetricAlgorithmFlow::getUseFromCreation(this, _, _) }
55+
SymmetricAlgorithmInstance() {
56+
this.isEncryptionCall() or this.isDecryptionCall() or this.isCreationCall()
57+
}
5458

55-
override string getRawAlgorithmName() { result = super.getSymmetricAlgorithm().getName() }
59+
override string getRawAlgorithmName() {
60+
result = super.getSymmetricAlgorithm().getType().getName()
61+
}
5662

5763
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
5864
if exists(symmetricAlgorithmNameToType(this.getRawAlgorithmName()))
5965
then result = symmetricAlgorithmNameToType(this.getRawAlgorithmName())
6066
else result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::OtherSymmetricCipherType())
6167
}
6268

63-
// The cipher mode is set by assigning it to the `Mode` property of the
64-
// symmetric algorithm.
6569
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() {
70+
super.isCreationCall() and
6671
result.(CipherModeLiteralInstance).getConsumer() = this.getCipherModeAlgorithmValueConsumer()
72+
or
73+
(super.isEncryptionCall() or super.isDecryptionCall()) and
74+
result = this
6775
}
6876

69-
// The padding mode is set by assigning it to the `Padding` property of the
70-
// symmetric algorithm.
7177
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() {
7278
result.(PaddingModeLiteralInstance).getConsumer() = this.getPaddingAlgorithmValueConsumer()
7379
}
7480

81+
// The padding mode is set by assigning it to the `Padding` property of the
82+
// symmetric algorithm. It can also be passed as an argument to `EncryptCbc`,
83+
// `EncryptCfb`, etc.
7584
Crypto::AlgorithmValueConsumer getPaddingAlgorithmValueConsumer() {
76-
result = SymmetricAlgorithmFlow::getUseFromCreation(this, _, _) and
85+
super.isCreationCall() and
86+
result = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this, _, _) and
7787
result instanceof PaddingPropertyWrite
88+
or
89+
(super.isEncryptionCall() or super.isDecryptionCall()) and
90+
result = super.getPaddingArg()
7891
}
7992

93+
// The cipher mode is set by assigning it to the `Mode` property of the
94+
// symmetric algorithm, or if this is an encryption/decryption call, it
95+
// is implicit in the method name.
8096
Crypto::AlgorithmValueConsumer getCipherModeAlgorithmValueConsumer() {
81-
result = SymmetricAlgorithmFlow::getUseFromCreation(this, _, _) and
97+
result = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this, _, _) and
8298
result instanceof CipherModePropertyWrite
8399
}
84100

85101
override int getKeySizeFixed() { none() }
86102

87103
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
104+
}
88105

89-
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
106+
/**
107+
* A call to an encryption or decryption API (e.g. `EncryptCbc` or `EncryptCfb`)
108+
* on a `SymmetricAlgorithm` instance.
109+
*
110+
* For these, the cipher mode is given by the method name.
111+
*/
112+
class SymmetricAlgorithmMode extends Crypto::ModeOfOperationAlgorithmInstance instanceof SymmetricAlgorithmUse
113+
{
114+
SymmetricAlgorithmMode() { this.isEncryptionCall() or this.isDecryptionCall() }
115+
116+
override string getRawModeAlgorithmName() {
117+
result = this.(SymmetricAlgorithmUse).getRawModeAlgorithmName()
118+
}
119+
120+
override Crypto::TBlockCipherModeOfOperationType getModeType() {
121+
if exists(modeNameToType(this.getRawModeAlgorithmName().toUpperCase()))
122+
then result = modeNameToType(this.getRawModeAlgorithmName().toUpperCase())
123+
else result = Crypto::OtherMode()
124+
}
90125
}
91126

92127
/**
@@ -113,7 +148,7 @@ class PaddingModeLiteralInstance extends Crypto::PaddingAlgorithmInstance instan
113148
}
114149

115150
/**
116-
* A padding mode literal, such as `PaddingMode.PKCS7`.
151+
* A cipher mode literal, such as `CipherMode.CBC`.
117152
*/
118153
class CipherModeLiteralInstance extends Crypto::ModeOfOperationAlgorithmInstance instanceof MemberConstantAccess
119154
{

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

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,39 @@ class CipherModePropertyWrite extends Crypto::AlgorithmValueConsumer instanceof
4444
}
4545

4646
/**
47-
* A call to a `SymmetricAlgorithm.CreateEncryptor` or `SymmetricAlgorithm.CreateDecryptor`
48-
* method that returns a `CryptoTransform` instance.
47+
* A padding mode argument passed to a symmetric algorithm method call.
4948
*/
50-
class SymmetricAlgorithmConsumer extends Crypto::AlgorithmValueConsumer instanceof CryptoTransformCreation
49+
class PaddingModeArgument extends Crypto::AlgorithmValueConsumer instanceof Expr {
50+
SymmetricAlgorithmUse use;
51+
52+
PaddingModeArgument() {
53+
(use.isEncryptionCall() or use.isDecryptionCall()) and
54+
this = use.getPaddingArg()
55+
}
56+
57+
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
58+
59+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
60+
result.(PaddingModeLiteralInstance).getConsumer() = this
61+
}
62+
}
63+
64+
/**
65+
* A qualified expression where the qualifier is a `SymmetricAlgorithm`
66+
* instance. (e.g. a call to `SymmetricAlgorithm.EncryptCbc` or
67+
* `SymmetricAlgorithm.CreateEncryptor`)
68+
*/
69+
class SymmetricAlgorithmConsumer extends Crypto::AlgorithmValueConsumer instanceof SymmetricAlgorithmUse
5170
{
71+
SymmetricAlgorithmConsumer() {
72+
super.isEncryptionCall() or super.isDecryptionCall() or super.isCreationCall()
73+
}
74+
5275
override Crypto::ConsumerInputDataFlowNode getInputNode() {
5376
result.asExpr() = super.getQualifier()
5477
}
5578

56-
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
57-
result.(SymmetricAlgorithmInstance).getConsumer() = this
58-
}
79+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this }
5980
}
6081

6182
/**

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

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,21 +423,23 @@ class SymmetricAlgorithmCreation extends MethodCall {
423423
this.getTarget().hasName("Create") and
424424
this.getQualifier().getType() instanceof SymmetricAlgorithm
425425
}
426-
427-
SymmetricAlgorithm getSymmetricAlgorithm() { result = this.getQualifier().getType() }
428426
}
429427

430428
class SymmetricAlgorithmUse extends QualifiableExpr {
431429
SymmetricAlgorithmUse() {
432430
this.getQualifier().getType() instanceof SymmetricAlgorithm and
433431
this.getQualifiedDeclaration()
434-
.hasName(["CreateEncryptor", "CreateDecryptor", "Key", "IV", "Padding", "Mode"])
432+
.hasName([
433+
"EncryptCbc", "DecryptCbc", "EncryptCfb", "DecryptCfb", "EncryptEcb", "DecryptEcb",
434+
"TryEncryptCbc", "TryDecryptCbc", "TryEncryptCfb", "TryDecryptCfb", "TryEncryptEcb",
435+
"TryDecryptEcb", "CreateEncryptor", "CreateDecryptor", "Key", "IV", "Padding", "Mode"
436+
])
435437
}
436438

437439
Expr getSymmetricAlgorithm() { result = this.getQualifier() }
438440

439441
predicate isIntermediate() {
440-
not this.getQualifiedDeclaration().hasName(["CreateEncryptor", "CreateDecryptor"])
442+
this.getQualifiedDeclaration().hasName(["Key", "IV", "Padding", "Mode"])
441443
}
442444

443445
// The key may be set by assigning it to the `Key` property of the symmetric algorithm.
@@ -459,6 +461,50 @@ class SymmetricAlgorithmUse extends QualifiableExpr {
459461
predicate isModeConsumer() {
460462
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Mode"
461463
}
464+
465+
predicate isCreationCall() {
466+
// TODO: Matching using `hasName` does not work here for some reason.
467+
this.getQualifiedDeclaration().getName().matches("Create%")
468+
}
469+
470+
predicate isEncryptionCall() {
471+
// TODO: Matching using `hasName` does not work here for some reason.
472+
this.getQualifiedDeclaration().getName().matches(["Encrypt%", "TryEncrypt%"])
473+
}
474+
475+
predicate isDecryptionCall() {
476+
// TODO: Matching using `hasName` does not work here for some reason.
477+
this.getQualifiedDeclaration().getName().matches(["Decrypt%", "TryDecrypt%"])
478+
}
479+
480+
string getRawModeAlgorithmName() {
481+
this.isEncryptionCall() and
482+
result = this.getQualifiedDeclaration().getName().splitAt("Encrypt", 1)
483+
or
484+
this.isDecryptionCall() and
485+
result = this.getQualifiedDeclaration().getName().splitAt("Decrypt", 1)
486+
}
487+
488+
Expr getInputArg() {
489+
(this.isEncryptionCall() or this.isDecryptionCall()) and
490+
result = this.(MethodCall).getArgument(0)
491+
}
492+
493+
Expr getIvArg() {
494+
(this.isEncryptionCall() or this.isDecryptionCall()) and
495+
this.getRawModeAlgorithmName().matches(["Cbc", "Cfb"]) and
496+
result = this.(MethodCall).getArgument(1)
497+
}
498+
499+
Expr getPaddingArg() {
500+
(this.isEncryptionCall() or this.isDecryptionCall()) and
501+
result = this.(MethodCall).getArgument(this.(MethodCall).getNumberOfArguments() - 1)
502+
}
503+
504+
Expr getOutput() {
505+
(this.isEncryptionCall() or this.isDecryptionCall()) and
506+
result = this
507+
}
462508
}
463509

464510
/**

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ module ModeLiteralFlow {
151151

152152
predicate isSink(DataFlow::Node sink) {
153153
sink.asExpr() instanceof PaddingPropertyWrite or
154+
sink.asExpr() instanceof PaddingModeArgument or
154155
sink.asExpr() instanceof CipherModePropertyWrite
155156
}
156157

@@ -165,9 +166,7 @@ module ModeLiteralFlow {
165166

166167
private module ModeLiteralFlow = DataFlow::Global<ModeLiteralConfig>;
167168

168-
SymmetricAlgorithmUse getConsumer(
169-
Expr mode, ModeLiteralFlow::PathNode source, ModeLiteralFlow::PathNode sink
170-
) {
169+
Expr getConsumer(Expr mode, ModeLiteralFlow::PathNode source, ModeLiteralFlow::PathNode sink) {
171170
source.getNode().asExpr() = mode and
172171
sink.getNode().asExpr() = result and
173172
ModeLiteralFlow::flowPath(source, sink)

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

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,57 @@ class HashOperationInstance extends Crypto::HashOperationInstance instanceof Has
5555
}
5656
}
5757

58+
/**
59+
* A call to an encryption or decryption API (e.g. `EncryptCbc` or `EncryptCfb`)
60+
* on a `SymmetricAlgorithm` instance.
61+
*/
62+
class SymmetricAlgorithmOperationInstance extends Crypto::KeyOperationInstance instanceof SymmetricAlgorithmUse
63+
{
64+
SymmetricAlgorithmOperationInstance() { super.isEncryptionCall() or super.isDecryptionCall() }
65+
66+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = this }
67+
68+
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
69+
if super.isEncryptionCall()
70+
then result = Crypto::TEncryptMode()
71+
else
72+
if super.isDecryptionCall()
73+
then result = Crypto::TDecryptMode()
74+
else result = Crypto::TUnknownKeyOperationMode()
75+
}
76+
77+
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
78+
result.asExpr() = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this, _, _) and
79+
result.asExpr().(SymmetricAlgorithmUse).isKeyConsumer()
80+
}
81+
82+
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
83+
result.asExpr() = super.getIvArg()
84+
}
85+
86+
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
87+
result.asExpr() = super.getInputArg()
88+
}
89+
90+
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
91+
result.asExpr() = super.getOutput()
92+
}
93+
}
94+
5895
/**
5996
* An instantiation of a `CryptoStream` object where the transform is a symmetric
6097
* encryption or decryption operation (e.g. an encryption transform created by a
6198
* call to `Aes.CreateEncryptor()`)
6299
*/
63100
class CryptoStreamOperationInstance extends Crypto::KeyOperationInstance instanceof CryptoStreamCreation
64101
{
65-
CryptoTransformCreation transform;
66-
67-
CryptoStreamOperationInstance() {
68-
transform = CryptoTransformFlow::getCreationFromUse(this) and
69-
(transform.isEncryptor() or transform.isDecryptor())
70-
}
71-
72-
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = transform }
102+
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = this.getCryptoTransform() }
73103

74104
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
75-
if transform.isEncryptor()
105+
if this.getCryptoTransform().isEncryptor()
76106
then result = Crypto::TEncryptMode()
77107
else
78-
if transform.isDecryptor()
108+
if this.getCryptoTransform().isDecryptor()
79109
then result = Crypto::TDecryptMode()
80110
else result = Crypto::TUnknownKeyOperationMode()
81111
}
@@ -84,10 +114,10 @@ class CryptoStreamOperationInstance extends Crypto::KeyOperationInstance instanc
84114
// If a key is explicitly provided as an argument when the transform is
85115
// created, this takes precedence over any key that may be set on the
86116
// symmetric algorithm instance.
87-
if exists(transform.getKeyArg())
88-
then result.asExpr() = transform.getKeyArg()
117+
if exists(this.getCryptoTransform().getKeyArg())
118+
then result.asExpr() = this.getCryptoTransform().getKeyArg()
89119
else (
90-
result.asExpr() = SymmetricAlgorithmFlow::getIntermediateUseFromUse(transform, _, _) and
120+
result.asExpr() = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this.getCryptoTransform(), _, _) and
91121
result.asExpr().(SymmetricAlgorithmUse).isKeyConsumer()
92122
)
93123
}
@@ -96,10 +126,10 @@ class CryptoStreamOperationInstance extends Crypto::KeyOperationInstance instanc
96126
// If an IV is explicitly provided as an argument when the transform is
97127
// created, this takes precedence over any IV that may be set on the
98128
// symmetric algorithm instance.
99-
if exists(transform.getIvArg())
100-
then result.asExpr() = transform.getIvArg()
129+
if exists(this.getCryptoTransform().getIvArg())
130+
then result.asExpr() = this.getCryptoTransform().getIvArg()
101131
else (
102-
result.asExpr() = SymmetricAlgorithmFlow::getIntermediateUseFromUse(transform, _, _) and
132+
result.asExpr() = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this.getCryptoTransform(), _, _) and
103133
result.asExpr().(SymmetricAlgorithmUse).isIvConsumer()
104134
)
105135
}
@@ -126,6 +156,11 @@ class CryptoStreamOperationInstance extends Crypto::KeyOperationInstance instanc
126156
result.asExpr() = this.getLaterWrappedStreamUse().getOutput()
127157
}
128158

159+
CryptoTransformCreation getCryptoTransform() {
160+
result = CryptoTransformFlow::getCreationFromUse(this) and
161+
(result.isEncryptor() or result.isDecryptor())
162+
}
163+
129164
// Gets either this stream, or a stream wrapped by this stream.
130165
StreamCreation getWrappedStreamCreation() {
131166
result = StreamFlow::getWrappedStreamCreation(this, _, _)

0 commit comments

Comments
 (0)