Skip to content

Commit 601d5d1

Browse files
committed
Merge branch 'tob/quantum-csharp' of github.com:trailofbits/codeql into tob/quantum-csharp
2 parents e344505 + 5fc267a commit 601d5d1

File tree

11 files changed

+406
-188
lines changed

11 files changed

+406
-188
lines changed

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,51 @@ class CipherModeLiteralInstance extends Crypto::ModeOfOperationAlgorithmInstance
135135
Crypto::AlgorithmValueConsumer getConsumer() { result = consumer }
136136
}
137137

138+
/**
139+
* A call to either `Encrypt` or `Decrypt` on an `AesGcm`, `AesCcm`, or
140+
* `ChaCha20Poly1305` instance. The algorithm is defined implicitly by this AST
141+
* node.
142+
*/
143+
class AeadAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance,
144+
Crypto::ModeOfOperationAlgorithmInstance instanceof AeadUse
145+
{
146+
override string getRawAlgorithmName() {
147+
super.getQualifier().getType().hasName("Aes%") and result = "Aes"
148+
or
149+
super.getQualifier().getType().hasName("ChaCha20%") and result = "ChaCha20"
150+
}
151+
152+
override string getRawModeAlgorithmName() {
153+
super.getQualifier().getType().getName() = "AesGcm" and result = "Gcm"
154+
or
155+
super.getQualifier().getType().getName() = "AesCcm" and result = "Ccm"
156+
}
157+
158+
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
159+
this.getRawAlgorithmName() = "Aes" and
160+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
161+
or
162+
this.getRawAlgorithmName() = "ChaCha20" and
163+
result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::CHACHA20())
164+
}
165+
166+
override Crypto::TBlockCipherModeOfOperationType getModeType() {
167+
this.getRawModeAlgorithmName() = "Gcm" and result = Crypto::GCM()
168+
or
169+
this.getRawModeAlgorithmName() = "Ccm" and result = Crypto::CCM()
170+
}
171+
172+
override int getKeySizeFixed() { none() }
173+
174+
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() }
175+
176+
override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this }
177+
178+
override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() }
179+
}
180+
138181
private Crypto::KeyOpAlg::Algorithm symmetricAlgorithmNameToType(string algorithmName) {
139-
algorithmName = "Aes" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
182+
algorithmName = "Aes%" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES())
140183
or
141184
algorithmName = "DES" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES())
142185
or

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,17 @@ class SymmetricAlgorithmConsumer extends Crypto::AlgorithmValueConsumer instance
5757
result.(SymmetricAlgorithmInstance).getConsumer() = this
5858
}
5959
}
60+
61+
/**
62+
* A call to either `Encrypt` or `Decrypt` on an `AesGcm`, `AesCcm` or
63+
* `ChaCha20Poly1305` instance. The algorithm is defined implicitly by this AST
64+
* node.
65+
*/
66+
class AeadAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof AeadUse {
67+
override Crypto::ConsumerInputDataFlowNode getInputNode() { none() }
68+
69+
override Crypto::AlgorithmInstance getAKnownAlgorithmSource() {
70+
// See `AeadAlgorithmInstance` for the algorithm instance.
71+
result = this
72+
}
73+
}

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

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,231 @@ private class ECDsaSigner extends SignerUse {
352352
private class RSASigner extends SignerUse {
353353
RSASigner() { this.getQualifier().getType() instanceof RSAClass }
354354
}
355+
356+
/**
357+
* An AEAD class, such as `AesGcm`, `AesCcm`, or `ChaCha20Poly1305`.
358+
*/
359+
class Aead extends Class {
360+
Aead() {
361+
this.hasFullyQualifiedName("System.Security.Cryptography",
362+
["AesGcm", "AesCcm", "ChaCha20Poly1305"])
363+
}
364+
}
365+
366+
class AeadCreation extends ObjectCreation {
367+
AeadCreation() { this.getObjectType() instanceof Aead }
368+
369+
Expr getKeyArg() { result = this.getArgument(0) }
370+
}
371+
372+
class AeadUse extends MethodCall {
373+
AeadUse() {
374+
this.getQualifier().getType() instanceof Aead and
375+
this.getTarget().hasName(["Encrypt", "Decrypt"])
376+
}
377+
378+
// One-shot API only.
379+
predicate isIntermediate() { none() }
380+
381+
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
382+
if this.isEncrypt()
383+
then result = Crypto::TEncryptMode()
384+
else
385+
if this.isDecrypt()
386+
then result = Crypto::TDecryptMode()
387+
else result = Crypto::TUnknownKeyOperationMode()
388+
}
389+
390+
predicate isEncrypt() { this.getTarget().getName() = "Encrypt" }
391+
392+
predicate isDecrypt() { this.getTarget().getName() = "Decrypt" }
393+
394+
Expr getNonceArg() { result = this.getArgument(0) }
395+
396+
Expr getMessageArg() { result = this.getArgument(1) }
397+
398+
Expr getOutputArg() {
399+
this.isEncrypt() and
400+
result = this.getArgument(2)
401+
or
402+
this.isDecrypt() and
403+
result = this.getArgument(3)
404+
}
405+
}
406+
407+
/**
408+
* A symmetric algorithm class, such as AES or DES.
409+
*/
410+
class SymmetricAlgorithm extends Class {
411+
SymmetricAlgorithm() {
412+
this.getABaseType().hasFullyQualifiedName("System.Security.Cryptography", "SymmetricAlgorithm")
413+
}
414+
415+
CryptoTransformCreation getCreateTransformCall() { result = this.getAMethod().getACall() }
416+
}
417+
418+
/**
419+
* A symmetric algorithm creation, such as `Aes.Create()`.
420+
*/
421+
class SymmetricAlgorithmCreation extends MethodCall {
422+
SymmetricAlgorithmCreation() {
423+
this.getTarget().hasName("Create") and
424+
this.getQualifier().getType() instanceof SymmetricAlgorithm
425+
}
426+
427+
SymmetricAlgorithm getSymmetricAlgorithm() { result = this.getQualifier().getType() }
428+
}
429+
430+
class SymmetricAlgorithmUse extends QualifiableExpr {
431+
SymmetricAlgorithmUse() {
432+
this.getQualifier().getType() instanceof SymmetricAlgorithm and
433+
this.getQualifiedDeclaration()
434+
.hasName(["CreateEncryptor", "CreateDecryptor", "Key", "IV", "Padding", "Mode"])
435+
}
436+
437+
Expr getSymmetricAlgorithm() { result = this.getQualifier() }
438+
439+
predicate isIntermediate() {
440+
not this.getQualifiedDeclaration().hasName(["CreateEncryptor", "CreateDecryptor"])
441+
}
442+
443+
// The key may be set by assigning it to the `Key` property of the symmetric algorithm.
444+
predicate isKeyConsumer() {
445+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Key"
446+
}
447+
448+
// The IV may be set by assigning it to the `IV` property of the symmetric algorithm.
449+
predicate isIvConsumer() {
450+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "IV"
451+
}
452+
453+
// The padding mode may be set by assigning it to the `Padding` property of the symmetric algorithm.
454+
predicate isPaddingConsumer() {
455+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Padding"
456+
}
457+
458+
// The cipher mode may be set by assigning it to the `Mode` property of the symmetric algorithm.
459+
predicate isModeConsumer() {
460+
this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Mode"
461+
}
462+
}
463+
464+
/**
465+
* A call to `CreateEncryptor` or `CreateDecryptor` on a `SymmetricAlgorithm`.
466+
*/
467+
class CryptoTransformCreation extends MethodCall {
468+
CryptoTransformCreation() {
469+
this.getTarget().hasName(["CreateEncryptor", "CreateDecryptor"]) and
470+
this.getQualifier().getType() instanceof SymmetricAlgorithm
471+
}
472+
473+
predicate isEncryptor() { this.getTarget().getName() = "CreateEncryptor" }
474+
475+
predicate isDecryptor() { this.getTarget().getName() = "CreateDecryptor" }
476+
477+
Expr getKeyArg() { result = this.getArgument(0) }
478+
479+
Expr getIvArg() { result = this.getArgument(1) }
480+
481+
SymmetricAlgorithm getSymmetricAlgorithm() { result = this.getQualifier().getType() }
482+
}
483+
484+
class CryptoStream extends Class {
485+
CryptoStream() { this.hasFullyQualifiedName("System.Security.Cryptography", "CryptoStream") }
486+
}
487+
488+
class CryptoStreamMode extends MemberConstant {
489+
CryptoStreamMode() {
490+
this.getDeclaringType()
491+
.hasFullyQualifiedName("System.Security.Cryptography", "CryptoStreamMode")
492+
}
493+
494+
predicate isRead() { this.getName() = "Read" }
495+
496+
predicate isWrite() { this.getName() = "Write" }
497+
}
498+
499+
class PaddingMode extends MemberConstant {
500+
PaddingMode() {
501+
this.getDeclaringType().hasFullyQualifiedName("System.Security.Cryptography", "PaddingMode")
502+
}
503+
}
504+
505+
class CipherMode extends MemberConstant {
506+
CipherMode() {
507+
this.getDeclaringType().hasFullyQualifiedName("System.Security.Cryptography", "CipherMode")
508+
}
509+
}
510+
511+
class Stream extends Class {
512+
Stream() { this.getABaseType().hasFullyQualifiedName("System.IO", "Stream") }
513+
}
514+
515+
/**
516+
* A `Stream` object creation.
517+
*/
518+
class StreamCreation extends ObjectCreation {
519+
StreamCreation() { this.getObjectType() instanceof Stream }
520+
521+
Expr getInputArg() {
522+
result = this.getAnArgument() and
523+
result.getType().hasFullyQualifiedName("System", "Byte[]")
524+
}
525+
526+
Expr getStreamArg() {
527+
result = this.getAnArgument() and
528+
result.getType() instanceof Stream
529+
}
530+
}
531+
532+
class StreamUse extends MethodCall {
533+
StreamUse() {
534+
this.getQualifier().getType() instanceof Stream and
535+
this.getTarget().hasName(["ToArray", "Write"])
536+
}
537+
538+
predicate isIntermediate() { this.getTarget().hasName("Write") }
539+
540+
Expr getInputArg() {
541+
this.isIntermediate() and
542+
result = this.getArgument(0)
543+
}
544+
545+
Expr getOutput() {
546+
not this.isIntermediate() and
547+
result = this
548+
}
549+
}
550+
551+
class CryptoStreamCreation extends ObjectCreation {
552+
CryptoStreamCreation() { this.getObjectType() instanceof CryptoStream }
553+
554+
Expr getStreamArg() { result = this.getArgument(0) }
555+
556+
Expr getTransformArg() { result = this.getArgument(1) }
557+
558+
Expr getModeArg() { result = this.getArgument(2) }
559+
560+
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
561+
if CryptoTransformFlow::getCreationFromUse(this.getTransformArg()).isEncryptor()
562+
then result = Crypto::TEncryptMode()
563+
else
564+
if CryptoTransformFlow::getCreationFromUse(this.getTransformArg()).isDecryptor()
565+
then result = Crypto::TDecryptMode()
566+
else result = Crypto::TUnknownKeyOperationMode()
567+
}
568+
}
569+
570+
class CryptoStreamUse extends MethodCall {
571+
CryptoStreamUse() {
572+
this.getQualifier().getType() instanceof CryptoStream and
573+
this.getTarget().hasName(["Write", "FlushFinalBlock", "FlushFinalBlockAsync", "Close"])
574+
}
575+
576+
predicate isIntermediate() { this.getTarget().getName() = "Write" }
577+
578+
Expr getInputArg() {
579+
this.isIntermediate() and
580+
result = this.getArgument(0)
581+
}
582+
}

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

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

6969
module HashCreateToUseFlow = CreationToUseFlow<HashAlgorithmCreateCall, HashUse>;
7070

71+
module CryptoStreamFlow = CreationToUseFlow<CryptoStreamCreation, CryptoStreamUse>;
72+
73+
module AeadFlow = CreationToUseFlow<AeadCreation, AeadUse>;
74+
75+
module SymmetricAlgorithmFlow =
76+
CreationToUseFlow<SymmetricAlgorithmCreation, SymmetricAlgorithmUse>;
77+
7178
/**
7279
* A flow analysis module that tracks the flow from a `CryptoStreamMode.READ` or
7380
* `CryptoStreamMode.WRITE` access to the corresponding `CryptoStream` object

0 commit comments

Comments
 (0)