Skip to content

Commit 45ae4d1

Browse files
committed
Added support for AesGcm and AesCcm
This commit also reorganizes the dotnet library to move utility classes into the private Cryptography module.
1 parent f3c436a commit 45ae4d1

File tree

5 files changed

+304
-188
lines changed

5 files changed

+304
-188
lines changed

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,42 @@ class CipherModeLiteralInstance extends Crypto::ModeOfOperationAlgorithmInstance
159159
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
160160
}
161161

162+
/**
163+
* A call to either `Encrypt` or `Decrypt` on an `AesGcm` or `AesCcm` instance.
164+
* The algorithm is defined implicitly by this AST node.
165+
*/
166+
class AesModeAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance,
167+
Crypto::ModeOfOperationAlgorithmInstance instanceof AesModeUse
168+
{
169+
override string getRawAlgorithmName() { result = "Aes" }
170+
171+
override string getRawModeAlgorithmName() {
172+
this.getRawAlgorithmName() = "AesGcm" and result = "Gcm"
173+
or
174+
this.getRawAlgorithmName() = "AesCcm" and result = "Ccm"
175+
}
176+
177+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
178+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
179+
}
180+
181+
override Crypto::TBlockCipherModeOfOperationType getModeType() {
182+
this.getRawAlgorithmName() = "AesGcm" and result = Crypto::GCM()
183+
or
184+
this.getRawAlgorithmName() = "AesCcm" and result = Crypto::CCM()
185+
}
186+
187+
override int getKeySizeFixed() { none() }
188+
189+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
190+
191+
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this }
192+
193+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
194+
}
195+
162196
private Crypto::KeyOpAlg::Algorithm symmetricAlgorithmNameToType(string algorithmName) {
163-
algorithmName = "Aes" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
197+
algorithmName = "Aes%" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
164198
or
165199
algorithmName = "DES" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
166200
or

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,13 @@ class SymmetricAlgorithmConsumer extends Crypto::AlgorithmValueConsumer instance
6969
result.(SymmetricAlgorithmInstance).getConsumer() = this
7070
}
7171
}
72+
73+
/**
74+
* A call to either `Encrypt` or `Decrypt` on an `AesGcm` or `AesCcm` instance.
75+
* The algorithm is defined implicitly by this AST node.
76+
*/
77+
class AesModeAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof AesModeUse {
78+
override Crypto::ConsumerInputDataFlowNode getInputNode() { none() }
79+
80+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this }
81+
}

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

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,225 @@ private class ECDsaSigner extends SignerUse {
310310
private class RSASigner extends SignerUse {
311311
RSASigner() { this.getQualifier().getType() instanceof RSAClass }
312312
}
313+
314+
class AesMode extends Class {
315+
AesMode() { this.hasFullyQualifiedName("System.Security.Cryptography", ["AesGcm", "AesCcm"]) }
316+
}
317+
318+
class AesModeCreation extends ObjectCreation {
319+
AesModeCreation() { this.getObjectType() instanceof AesMode }
320+
321+
Expr getKeyArg() { result = this.getArgument(0) }
322+
}
323+
324+
class AesModeUse extends MethodCall {
325+
AesModeUse() {
326+
this.getQualifier().getType() instanceof AesMode and
327+
this.getTarget().hasName(["Encrypt", "Decrypt"])
328+
}
329+
330+
// One-shot API only.
331+
predicate isIntermediate() { none() }
332+
333+
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
334+
if this.isEncrypt()
335+
then result = Crypto::TEncryptMode()
336+
else
337+
if this.isDecrypt()
338+
then result = Crypto::TDecryptMode()
339+
else result = Crypto::TUnknownKeyOperationMode()
340+
}
341+
342+
predicate isEncrypt() { this.getTarget().getName() = "Encrypt" }
343+
344+
predicate isDecrypt() { this.getTarget().getName() = "Decrypt" }
345+
346+
Expr getNonceArg() { result = this.getArgument(0) }
347+
348+
Expr getMessageArg() { result = this.getArgument(1) }
349+
350+
Expr getOutputArg() {
351+
this.isEncrypt() and
352+
result = this.getArgument(2)
353+
or
354+
this.isDecrypt() and
355+
result = this.getArgument(3)
356+
}
357+
}
358+
359+
/**
360+
* A symmetric algorithm class, such as AES or DES.
361+
*/
362+
class SymmetricAlgorithm extends Class {
363+
SymmetricAlgorithm() {
364+
this.getABaseType().hasFullyQualifiedName("System.Security.Cryptography", "SymmetricAlgorithm")
365+
}
366+
367+
CryptoTransformCreation getCreateTransformCall() { result = this.getAMethod().getACall() }
368+
}
369+
370+
/**
371+
* A symmetric algorithm creation, such as `Aes.Create()`.
372+
*/
373+
class SymmetricAlgorithmCreation extends MethodCall {
374+
SymmetricAlgorithmCreation() {
375+
this.getTarget().hasName("Create") and
376+
this.getQualifier().getType() instanceof SymmetricAlgorithm
377+
}
378+
379+
SymmetricAlgorithm getSymmetricAlgorithm() { result = this.getQualifier().getType() }
380+
}
381+
382+
class SymmetricAlgorithmUse extends QualifiableExpr {
383+
SymmetricAlgorithmUse() {
384+
this.getQualifier().getType() instanceof SymmetricAlgorithm and
385+
this.getQualifiedDeclaration()
386+
.hasName(["CreateEncryptor", "CreateDecryptor", "Key", "IV", "Padding", "Mode"])
387+
}
388+
389+
Expr getSymmetricAlgorithm() { result = this.getQualifier() }
390+
391+
predicate isIntermediate() {
392+
not this.getQualifiedDeclaration().hasName(["CreateEncryptor", "CreateDecryptor"])
393+
}
394+
395+
// The key may be set by assigning it to the `Key` property of the symmetric algorithm.
396+
predicate isKeyConsumer() {
397+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Key"
398+
}
399+
400+
// The IV may be set by assigning it to the `IV` property of the symmetric algorithm.
401+
predicate isIvConsumer() {
402+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "IV"
403+
}
404+
405+
// The padding mode may be set by assigning it to the `Padding` property of the symmetric algorithm.
406+
predicate isPaddingConsumer() {
407+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Padding"
408+
}
409+
410+
// The cipher mode may be set by assigning it to the `Mode` property of the symmetric algorithm.
411+
predicate isModeConsumer() {
412+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Mode"
413+
}
414+
}
415+
416+
/**
417+
* A call to `CreateEncryptor` or `CreateDecryptor` on a `SymmetricAlgorithm`.
418+
*/
419+
class CryptoTransformCreation extends MethodCall {
420+
CryptoTransformCreation() {
421+
this.getTarget().hasName(["CreateEncryptor", "CreateDecryptor"]) and
422+
this.getQualifier().getType() instanceof SymmetricAlgorithm
423+
}
424+
425+
predicate isEncryptor() { this.getTarget().getName() = "CreateEncryptor" }
426+
427+
predicate isDecryptor() { this.getTarget().getName() = "CreateDecryptor" }
428+
429+
Expr getKeyArg() { result = this.getArgument(0) }
430+
431+
Expr getIvArg() { result = this.getArgument(1) }
432+
433+
SymmetricAlgorithm getSymmetricAlgorithm() { result = this.getQualifier().getType() }
434+
}
435+
436+
class CryptoStream extends Class {
437+
CryptoStream() { this.hasFullyQualifiedName("System.Security.Cryptography", "CryptoStream") }
438+
}
439+
440+
class CryptoStreamMode extends MemberConstant {
441+
CryptoStreamMode() {
442+
this.getDeclaringType()
443+
.hasFullyQualifiedName("System.Security.Cryptography", "CryptoStreamMode")
444+
}
445+
446+
predicate isRead() { this.getName() = "Read" }
447+
448+
predicate isWrite() { this.getName() = "Write" }
449+
}
450+
451+
class PaddingMode extends MemberConstant {
452+
PaddingMode() {
453+
this.getDeclaringType().hasFullyQualifiedName("System.Security.Cryptography", "PaddingMode")
454+
}
455+
}
456+
457+
class CipherMode extends MemberConstant {
458+
CipherMode() {
459+
this.getDeclaringType().hasFullyQualifiedName("System.Security.Cryptography", "CipherMode")
460+
}
461+
}
462+
463+
class Stream extends Class {
464+
Stream() { this.getABaseType().hasFullyQualifiedName("System.IO", "Stream") }
465+
}
466+
467+
/**
468+
* A `Stream` object creation.
469+
*/
470+
class StreamCreation extends ObjectCreation {
471+
StreamCreation() { this.getObjectType() instanceof Stream }
472+
473+
Expr getInputArg() {
474+
result = this.getAnArgument() and
475+
result.getType().hasFullyQualifiedName("System", "Byte[]")
476+
}
477+
478+
Expr getStreamArg() {
479+
result = this.getAnArgument() and
480+
result.getType() instanceof Stream
481+
}
482+
}
483+
484+
class StreamUse extends MethodCall {
485+
StreamUse() {
486+
this.getQualifier().getType() instanceof Stream and
487+
this.getTarget().hasName(["ToArray", "Write"])
488+
}
489+
490+
predicate isIntermediate() { this.getTarget().hasName("Write") }
491+
492+
Expr getInputArg() {
493+
this.isIntermediate() and
494+
result = this.getArgument(0)
495+
}
496+
497+
Expr getOutput() {
498+
not this.isIntermediate() and
499+
result = this
500+
}
501+
}
502+
503+
class CryptoStreamCreation extends ObjectCreation {
504+
CryptoStreamCreation() { this.getObjectType() instanceof CryptoStream }
505+
506+
Expr getStreamArg() { result = this.getArgument(0) }
507+
508+
Expr getTransformArg() { result = this.getArgument(1) }
509+
510+
Expr getModeArg() { result = this.getArgument(2) }
511+
512+
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
513+
if CryptoTransformFlow::getCreationFromUse(this.getTransformArg()).isEncryptor()
514+
then result = Crypto::TEncryptMode()
515+
else
516+
if CryptoTransformFlow::getCreationFromUse(this.getTransformArg()).isDecryptor()
517+
then result = Crypto::TDecryptMode()
518+
else result = Crypto::TUnknownKeyOperationMode()
519+
}
520+
}
521+
522+
class CryptoStreamUse extends MethodCall {
523+
CryptoStreamUse() {
524+
this.getQualifier().getType() instanceof CryptoStream and
525+
this.getTarget().hasName(["Write", "FlushFinalBlock", "FlushFinalBlockAsync", "Close"])
526+
}
527+
528+
predicate isIntermediate() { this.getTarget().getName() = "Write" }
529+
530+
Expr getInputArg() {
531+
this.isIntermediate() and
532+
result = this.getArgument(0)
533+
}
534+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ module SigningCreateToUseFlow = CreationToUseFlow<SigningCreateCall, SignerUse>;
8282

8383
module HashCreateToUseFlow = CreationToUseFlow<HashAlgorithmCreateCall, HashUse>;
8484

85+
module CryptoStreamFlow = CreationToUseFlow<CryptoStreamCreation, CryptoStreamUse>;
86+
87+
module AesModeFlow = CreationToUseFlow<AesModeCreation, AesModeUse>;
88+
89+
module SymmetricAlgorithmFlow =
90+
CreationToUseFlow<SymmetricAlgorithmCreation, SymmetricAlgorithmUse>;
91+
8592
/**
8693
* A flow analysis module that tracks the flow from a `CryptoStreamMode.READ` or
8794
* `CryptoStreamMode.WRITE` access to the corresponding `CryptoStream` object

0 commit comments

Comments
 (0)