diff --git a/csharp/ql/lib/experimental/quantum/Language.qll b/csharp/ql/lib/experimental/quantum/Language.qll new file mode 100644 index 000000000000..29f7fba7ccaf --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/Language.qll @@ -0,0 +1,183 @@ +private import csharp as Language +private import semmle.code.csharp.dataflow.DataFlow +private import codeql.quantum.experimental.Model + +private class UnknownLocation extends Language::Location { + UnknownLocation() { this.getFile().getAbsolutePath() = "" } +} + +/** + * A dummy location which is used when something doesn't have a location in + * the source code but needs to have a `Location` associated with it. There + * may be several distinct kinds of unknown locations. For example: one for + * expressions, one for statements and one for other program elements. + */ +private class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +module CryptoInput implements InputSig { + class DataFlowNode = Language::DataFlow::Node; + + class LocatableElement = Language::Element; + + class UnknownLocation = UnknownDefaultLocation; + + string locationToFileBaseNameAndLineNumberString(Language::Location location) { + result = location.getFile().getBaseName() + ":" + location.getStartLine() + } + + LocatableElement dfn_to_element(Language::DataFlow::Node node) { + result = node.asExpr() or + result = node.asParameter() + } + + predicate artifactOutputFlowsToGenericInput( + Language::DataFlow::Node artifactOutput, Language::DataFlow::Node otherInput + ) { + ArtifactFlow::flow(artifactOutput, otherInput) + } +} + +// Instantiate the `CryptographyBase` module +module Crypto = CryptographyBase; + +/** + * An additional flow step in generic data-flow configurations. + * Where a step is an edge between nodes `n1` and `n2`, + * `this` = `n1` and `getOutput()` = `n2`. + * + * FOR INTERNAL MODELING USE ONLY. + */ +abstract class AdditionalFlowInputStep extends DataFlow::Node { + abstract DataFlow::Node getOutput(); + + final DataFlow::Node getInput() { result = this } +} + +/** + * Generic data source to node input configuration + */ +module GenericDataSourceFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::GenericSourceInstance i).getOutputNode() + } + + predicate isSink(DataFlow::Node sink) { + sink = any(Crypto::FlowAwareElement other).getInputNode() + } + + predicate isBarrierOut(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getInputNode() + } + + predicate isBarrierIn(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getOutputNode() + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + } +} + +module ArtifactFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Crypto::ArtifactInstance artifact).getOutputNode() + } + + predicate isSink(DataFlow::Node sink) { + sink = any(Crypto::FlowAwareElement other).getInputNode() + } + + predicate isBarrierOut(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getInputNode() + } + + predicate isBarrierIn(DataFlow::Node node) { + node = any(Crypto::FlowAwareElement element).getOutputNode() + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + node1.(AdditionalFlowInputStep).getOutput() = node2 + } +} + +module GenericDataSourceFlow = TaintTracking::Global; + +module ArtifactFlow = DataFlow::Global; + +/** + * A method access that returns random data or writes random data to an argument. + */ +abstract class RandomnessSource extends MethodCall { + /** + * Gets the expression representing the output of this source. + */ + abstract Expr getOutput(); + + /** + * Gets the type of the source of randomness used by this call. + */ + Type getGenerator() { result = this.getQualifier().getType() } +} + +/** + * A call to `System.Security.Cryptography.RandomNumberGenerator.GetBytes`. + */ +class SecureRandomnessSource extends RandomnessSource { + SecureRandomnessSource() { + this.getTarget() + .hasFullyQualifiedName("System.Security.Cryptography", "RandomNumberGenerator", "GetBytes") + } + + override Expr getOutput() { result = this.getArgument(0) } +} + +/** + * A call to `System.Random.NextBytes`. + */ +class InsecureRandomnessSource extends RandomnessSource { + InsecureRandomnessSource() { + this.getTarget().hasFullyQualifiedName("System", "Random", "NextBytes") + } + + override Expr getOutput() { result = this.getArgument(0) } +} + +/** + * An instance of random number generation, modeled as the expression tied to an + * output node (i.e., the RNG output) + */ +abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance { + override DataFlow::Node getOutputNode() { result.asExpr() = this } +} + +/** + * An output instance from the system cryptographically secure RNG. + */ +class SecureRandomnessInstance extends RandomnessInstance { + RandomnessSource source; + + SecureRandomnessInstance() { + source instanceof SecureRandomnessSource and + this = source.getOutput() + } + + override string getGeneratorName() { result = source.getGenerator().getName() } +} + +/** + * An output instance from an insecure RNG. + */ +class InsecureRandomnessInstance extends RandomnessInstance { + RandomnessSource source; + + InsecureRandomnessInstance() { + not source instanceof SecureRandomnessSource and + this = source.getOutput() + } + + override string getGeneratorName() { result = source.getGenerator().getName() } +} + +import dotnet diff --git a/csharp/ql/lib/experimental/quantum/dotnet.qll b/csharp/ql/lib/experimental/quantum/dotnet.qll new file mode 100644 index 000000000000..463f00f1ed18 --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/dotnet.qll @@ -0,0 +1,3 @@ +import dotnet.AlgorithmValueConsumers +import dotnet.AlgorithmInstances +import dotnet.OperationInstances diff --git a/csharp/ql/lib/experimental/quantum/dotnet/AlgorithmInstances.qll b/csharp/ql/lib/experimental/quantum/dotnet/AlgorithmInstances.qll new file mode 100644 index 000000000000..fbee2bb4205d --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/dotnet/AlgorithmInstances.qll @@ -0,0 +1,246 @@ +private import csharp +private import experimental.quantum.Language +private import AlgorithmValueConsumers +private import OperationInstances +private import Cryptography +private import FlowAnalysis + +class NamedCurveAlgorithmInstance extends Crypto::EllipticCurveInstance instanceof NamedCurvePropertyAccess +{ + override string getRawEllipticCurveName() { result = super.getCurveName() } + + override Crypto::TEllipticCurveType getEllipticCurveType() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), _, result) + } + + override int getKeySize() { + Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getRawEllipticCurveName(), result, _) + } +} + +abstract class SigningAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance { + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { none() } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override int getKeySizeFixed() { none() } +} + +class HashAlgorithmNameInstance extends Crypto::HashAlgorithmInstance instanceof HashAlgorithmName { + HashAlgorithmNameConsumer consumer; + + HashAlgorithmNameInstance() { + HashAlgorithmNameToUse::flow(DataFlow::exprNode(this), consumer.getInputNode()) + } + + override Crypto::THashType getHashFamily() { result = this.(HashAlgorithmName).getHashFamily() } + + override string getRawHashAlgorithmName() { result = super.getAlgorithmName() } + + override int getFixedDigestLength() { result = this.(HashAlgorithmName).getFixedDigestLength() } + + Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } +} + +/** + * A call to an encryption, decryption, or transform creation API (e.g. + * `EncryptCbc` or `CreateEncryptor`) on a `SymmetricAlgorithm` instance. + */ +class SymmetricAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance instanceof SymmetricAlgorithmUse +{ + SymmetricAlgorithmInstance() { + this.isEncryptionCall() or this.isDecryptionCall() or this.isCreationCall() + } + + override string getRawAlgorithmName() { + result = super.getSymmetricAlgorithm().getType().getName() + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + if exists(symmetricAlgorithmNameToType(this.getRawAlgorithmName())) + then result = symmetricAlgorithmNameToType(this.getRawAlgorithmName()) + else result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::OtherSymmetricCipherType()) + } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { + super.isCreationCall() and + result.(CipherModeLiteralInstance).getConsumer() = this.getCipherModeAlgorithmValueConsumer() + or + (super.isEncryptionCall() or super.isDecryptionCall()) and + result = this + } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { + result.(PaddingModeLiteralInstance).getConsumer() = this.getPaddingAlgorithmValueConsumer() + } + + // The padding mode is set by assigning it to the `Padding` property of the + // symmetric algorithm. It can also be passed as an argument to `EncryptCbc`, + // `EncryptCfb`, etc. + Crypto::AlgorithmValueConsumer getPaddingAlgorithmValueConsumer() { + super.isCreationCall() and + result = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this, _, _) and + result instanceof PaddingPropertyWrite + or + (super.isEncryptionCall() or super.isDecryptionCall()) and + result = super.getPaddingArg() + } + + // The cipher mode is set by assigning it to the `Mode` property of the + // symmetric algorithm, or if this is an encryption/decryption call, it + // is implicit in the method name. + Crypto::AlgorithmValueConsumer getCipherModeAlgorithmValueConsumer() { + result = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this, _, _) and + result instanceof CipherModePropertyWrite + } + + override int getKeySizeFixed() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } +} + +/** + * A call to an encryption or decryption API (e.g. `EncryptCbc` or `EncryptCfb`) + * on a `SymmetricAlgorithm` instance. + * + * For these, the cipher mode is given by the method name. + */ +class SymmetricAlgorithmMode extends Crypto::ModeOfOperationAlgorithmInstance instanceof SymmetricAlgorithmUse +{ + SymmetricAlgorithmMode() { this.isEncryptionCall() or this.isDecryptionCall() } + + override string getRawModeAlgorithmName() { + result = this.(SymmetricAlgorithmUse).getRawModeAlgorithmName() + } + + override Crypto::TBlockCipherModeOfOperationType getModeType() { + if exists(modeNameToType(this.getRawModeAlgorithmName().toUpperCase())) + then result = modeNameToType(this.getRawModeAlgorithmName().toUpperCase()) + else result = Crypto::OtherMode() + } +} + +/** + * A padding mode literal, such as `PaddingMode.PKCS7`. + */ +class PaddingModeLiteralInstance extends Crypto::PaddingAlgorithmInstance instanceof MemberConstantAccess +{ + Crypto::AlgorithmValueConsumer consumer; + + PaddingModeLiteralInstance() { + this = any(PaddingMode mode).getAnAccess() and + consumer = ModeLiteralFlow::getConsumer(this, _, _) + } + + override string getRawPaddingAlgorithmName() { result = super.getTarget().getName() } + + override Crypto::TPaddingType getPaddingType() { + if exists(paddingNameToType(this.getRawPaddingAlgorithmName())) + then result = paddingNameToType(this.getRawPaddingAlgorithmName()) + else result = Crypto::OtherPadding() + } + + Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } +} + +/** + * A cipher mode literal, such as `CipherMode.CBC`. + */ +class CipherModeLiteralInstance extends Crypto::ModeOfOperationAlgorithmInstance instanceof MemberConstantAccess +{ + Crypto::AlgorithmValueConsumer consumer; + + CipherModeLiteralInstance() { + this = any(CipherMode mode).getAnAccess() and + consumer = ModeLiteralFlow::getConsumer(this, _, _) + } + + override string getRawModeAlgorithmName() { result = super.getTarget().getName() } + + override Crypto::TBlockCipherModeOfOperationType getModeType() { + if exists(modeNameToType(this.getRawModeAlgorithmName())) + then result = modeNameToType(this.getRawModeAlgorithmName()) + else result = Crypto::OtherMode() + } + + Crypto::AlgorithmValueConsumer getConsumer() { result = consumer } +} + +/** + * A call to either `Encrypt` or `Decrypt` on an `AesGcm`, `AesCcm`, or + * `ChaCha20Poly1305` instance. The algorithm is defined implicitly by this AST + * node. + */ +class AeadAlgorithmInstance extends Crypto::KeyOperationAlgorithmInstance, + Crypto::ModeOfOperationAlgorithmInstance instanceof AeadUse +{ + override string getRawAlgorithmName() { + super.getQualifier().getType().hasName("Aes%") and result = "Aes" + or + super.getQualifier().getType().hasName("ChaCha20%") and result = "ChaCha20" + } + + override string getRawModeAlgorithmName() { + super.getQualifier().getType().getName() = "AesGcm" and result = "Gcm" + or + super.getQualifier().getType().getName() = "AesCcm" and result = "Ccm" + } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + this.getRawAlgorithmName() = "Aes" and + result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) + or + this.getRawAlgorithmName() = "ChaCha20" and + result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::CHACHA20()) + } + + override Crypto::TBlockCipherModeOfOperationType getModeType() { + this.getRawModeAlgorithmName() = "Gcm" and result = Crypto::GCM() + or + this.getRawModeAlgorithmName() = "Ccm" and result = Crypto::CCM() + } + + override int getKeySizeFixed() { none() } + + override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() { none() } + + override Crypto::ModeOfOperationAlgorithmInstance getModeOfOperationAlgorithm() { result = this } + + override Crypto::PaddingAlgorithmInstance getPaddingAlgorithm() { none() } +} + +bindingset[algorithmName] +private Crypto::KeyOpAlg::Algorithm symmetricAlgorithmNameToType(string algorithmName) { + algorithmName.matches("Aes%") and + result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) + or + algorithmName = "DES" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) + or + algorithmName = "RC2" and result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::RC2()) + or + algorithmName = "Rijndael" and + result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::AES()) + or + algorithmName = "TripleDES" and + result = Crypto::KeyOpAlg::TSymmetricCipher(Crypto::KeyOpAlg::DES()) +} + +private Crypto::TPaddingType paddingNameToType(string paddingName) { + paddingName = "ANSIX923" and result = Crypto::ANSI_X9_23() + or + paddingName = "None" and result = Crypto::NoPadding() + or + paddingName = "PKCS7" and result = Crypto::PKCS7() +} + +private Crypto::TBlockCipherModeOfOperationType modeNameToType(string modeName) { + modeName = "CBC" and result = Crypto::CBC() + or + modeName = "CFB" and result = Crypto::CFB() + or + modeName = "ECB" and result = Crypto::ECB() + or + modeName = "OFB" and result = Crypto::OFB() +} diff --git a/csharp/ql/lib/experimental/quantum/dotnet/AlgorithmValueConsumers.qll b/csharp/ql/lib/experimental/quantum/dotnet/AlgorithmValueConsumers.qll new file mode 100644 index 000000000000..0464d184aaab --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/dotnet/AlgorithmValueConsumers.qll @@ -0,0 +1,94 @@ +private import csharp +private import experimental.quantum.Language +private import AlgorithmInstances +private import OperationInstances +private import Cryptography + +class HashAlgorithmNameConsumer extends Crypto::AlgorithmValueConsumer { + HashAlgorithmNameUser call; + + HashAlgorithmNameConsumer() { this = call.getHashAlgorithmNameUser() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + exists(HashAlgorithmNameInstance l | l.getConsumer() = this and result = l) + } +} + +/** + * A write access to the `Padding` property of a `SymmetricAlgorithm` instance. + */ +class PaddingPropertyWrite extends Crypto::AlgorithmValueConsumer instanceof SymmetricAlgorithmUse { + PaddingPropertyWrite() { super.isPaddingConsumer() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(PaddingModeLiteralInstance).getConsumer() = this + } +} + +/** + * A write access to the `Mode` property of a `SymmetricAlgorithm` instance. + */ +class CipherModePropertyWrite extends Crypto::AlgorithmValueConsumer instanceof SymmetricAlgorithmUse +{ + CipherModePropertyWrite() { super.isModeConsumer() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(CipherModeLiteralInstance).getConsumer() = this + } +} + +/** + * A padding mode argument passed to a symmetric algorithm method call. + */ +class PaddingModeArgument extends Crypto::AlgorithmValueConsumer instanceof Expr { + SymmetricAlgorithmUse use; + + PaddingModeArgument() { + (use.isEncryptionCall() or use.isDecryptionCall()) and + this = use.getPaddingArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + result.(PaddingModeLiteralInstance).getConsumer() = this + } +} + +/** + * A qualified expression where the qualifier is a `SymmetricAlgorithm` + * instance. (e.g. a call to `SymmetricAlgorithm.EncryptCbc` or + * `SymmetricAlgorithm.CreateEncryptor`) + */ +class SymmetricAlgorithmConsumer extends Crypto::AlgorithmValueConsumer instanceof SymmetricAlgorithmUse +{ + SymmetricAlgorithmConsumer() { + super.isEncryptionCall() or super.isDecryptionCall() or super.isCreationCall() + } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { + result.asExpr() = super.getQualifier() + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } +} + +/** + * A call to either `Encrypt` or `Decrypt` on an `AesGcm`, `AesCcm` or + * `ChaCha20Poly1305` instance. The algorithm is defined implicitly by this AST + * node. + */ +class AeadAlgorithmValueConsumer extends Crypto::AlgorithmValueConsumer instanceof AeadUse { + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { + // See `AeadAlgorithmInstance` for the algorithm instance. + result = this + } +} diff --git a/csharp/ql/lib/experimental/quantum/dotnet/Cryptography.qll b/csharp/ql/lib/experimental/quantum/dotnet/Cryptography.qll new file mode 100644 index 000000000000..1647a26dc93f --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/dotnet/Cryptography.qll @@ -0,0 +1,724 @@ +private import csharp +private import experimental.quantum.Language +private import FlowAnalysis + +class CryptographyType extends Type { + CryptographyType() { this.hasFullyQualifiedName("System.Security.Cryptography", _) } +} + +class EcParameters extends CryptographyType { + EcParameters() { this.hasName("ECParameters") } +} + +class RsaParameters extends CryptographyType { + RsaParameters() { this.hasName("RSAParameters") } +} + +class EcCurve extends CryptographyType { + EcCurve() { this.hasName("ECCurve") } +} + +class HashAlgorithmType extends CryptographyType { + HashAlgorithmType() { + this.hasName([ + "MD5", + "RIPEMD160", + "SHA1", + "SHA256", + "SHA384", + "SHA512", + "SHA3_256", + "SHA3_384", + "SHA3_512" + ]) + } +} + +// This class models Create calls for the ECDsa and RSA classes in .NET. +class CryptographyCreateCall extends MethodCall { + CryptographyCreateCall() { + this.getTarget().hasName("Create") and + this.getQualifier().getType() instanceof CryptographyType + } + + Expr getAlgorithmArg() { + if this.getArgument(0).getType().getName().matches("%Parameters") + then result = this.getArgument(0) + else result = this + } + + Expr getKeyConsumer() { + if this.getArgument(0).getType().getName().matches("%Parameters") + then result = this.getArgument(0) + else result = this + } +} + +class EcdsaType extends CryptographyType { + EcdsaType() { this.hasName("ECDsa") } +} + +class RsaType extends CryptographyType { + RsaType() { this.hasName("RSA") } +} + +class RsaPkcs1Type extends CryptographyType { + RsaPkcs1Type() { this.getName().matches("RSAPKCS1Signature%") } +} + +class EcdsaCreateCall extends CryptographyCreateCall { + EcdsaCreateCall() { this.getQualifier().getType() instanceof EcdsaType } +} + +// This class is used to model the `ECDsa.Create(ECParameters)` call +class EcdsaCreateCallWithParameters extends EcdsaCreateCall { + EcdsaCreateCallWithParameters() { this.getArgument(0).getType() instanceof EcParameters } +} + +class EcdsaCreateCallWithECCurve extends EcdsaCreateCall { + EcdsaCreateCallWithECCurve() { this.getArgument(0).getType() instanceof EcCurve } +} + +class RsaCreateCall extends CryptographyCreateCall { + RsaCreateCall() { this.getQualifier().getType().hasName("RSA") } +} + +class SigningCreateCall extends CryptographyCreateCall { + SigningCreateCall() { + this instanceof EcdsaCreateCall or + this instanceof RsaCreateCall + } +} + +/** + * A call to create on an hash algorithm instance. + * The hash algorithm is defined by the qualifier. + */ +class HashAlgorithmCreateCall extends Crypto::AlgorithmValueConsumer instanceof CryptographyCreateCall +{ + HashAlgorithmCreateCall() { super.getQualifier().getType() instanceof HashAlgorithmType } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = super.getQualifier() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} + +class HashAlgorithmQualifier extends Crypto::AlgorithmValueConsumer, Crypto::HashAlgorithmInstance instanceof Expr +{ + HashAlgorithmQualifier() { this = any(HashUse c).getQualifier() } + + override Crypto::THashType getHashFamily() { + result = getHashFamily(this.getRawHashAlgorithmName()) + } + + override string getRawHashAlgorithmName() { result = super.getType().getName() } + + override int getFixedDigestLength() { + hashAlgorithmToFamily(this.getRawHashAlgorithmName(), _, result) + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} + +class NamedCurvePropertyAccess extends PropertyAccess { + string curveName; + + NamedCurvePropertyAccess() { + super.getType().getName() = "ECCurve" and + ecCurveNameMapping(super.getProperty().toString().toUpperCase(), curveName) + } + + string getCurveName() { result = curveName } +} + +class HashAlgorithmNameType extends CryptographyType { + HashAlgorithmNameType() { this.hasName("HashAlgorithmName") } +} + +class HashAlgorithmName extends PropertyAccess { + string algorithmName; + + HashAlgorithmName() { + this.getType() instanceof HashAlgorithmNameType and + this.getProperty().getName() = algorithmName + } + + string getAlgorithmName() { result = algorithmName } + + Crypto::THashType getHashFamily() { result = getHashFamily(this.getAlgorithmName()) } + + int getFixedDigestLength() { hashAlgorithmToFamily(this.getAlgorithmName(), _, result) } +} + +bindingset[name] +Crypto::THashType getHashFamily(string name) { + if hashAlgorithmToFamily(name, _, _) + then hashAlgorithmToFamily(name, result, _) + else result = Crypto::OtherHashType() +} + +private predicate hashAlgorithmToFamily( + string hashName, Crypto::THashType hashFamily, int digestLength +) { + hashName = "MD5" and hashFamily = Crypto::MD5() and digestLength = 128 + or + hashName = "SHA1" and hashFamily = Crypto::SHA1() and digestLength = 160 + or + hashName = "SHA256" and hashFamily = Crypto::SHA2() and digestLength = 256 + or + hashName = "SHA384" and hashFamily = Crypto::SHA2() and digestLength = 384 + or + hashName = "SHA512" and hashFamily = Crypto::SHA2() and digestLength = 512 + or + hashName = "SHA3_256" and hashFamily = Crypto::SHA3() and digestLength = 256 + or + hashName = "SHA3_384" and hashFamily = Crypto::SHA3() and digestLength = 384 + or + hashName = "SHA3_512" and hashFamily = Crypto::SHA3() and digestLength = 512 + or + hashName = "RIPEMD160" and hashFamily = Crypto::RIPEMD160() and digestLength = 160 +} + +class HashAlgorithmNameUser extends MethodCall { + Expr arg; + + HashAlgorithmNameUser() { + arg = this.getAnArgument() and + arg.getType() instanceof HashAlgorithmNameType + } + + Expr getHashAlgorithmNameUser() { result = arg } +} + +/** + * Private predicate mapping NIST names to SEC names and leaving all others the same. + */ +bindingset[nist] +private predicate ecCurveNameMapping(string nist, string secp) { + if nist.matches("NIST%") + then + nist = "NISTP256" and secp = "secp256r1" + or + nist = "NISTP384" and secp = "secp384r1" + or + nist = "NISTP521" and secp = "secp521r1" + else secp = nist +} + +// OPERATION INSTANCES +private class EcdsaClass extends CryptographyType { + EcdsaClass() { this.hasName("ECDsa") } +} + +private class RsaClass extends CryptographyType { + RsaClass() { this.hasName("RSA") } +} + +private class RsaPkcs1SignatureFormatter extends CryptographyType { + RsaPkcs1SignatureFormatter() { this.hasName("RSAPKCS1SignatureFormatter") } +} + +private class RsaPkcs1SignatureDeformatter extends CryptographyType { + RsaPkcs1SignatureDeformatter() { this.hasName("RSAPKCS1SignatureDeformatter") } +} + +private class SignerType extends Type { + SignerType() { + this instanceof EcdsaClass or + this instanceof RsaClass or + this instanceof RsaPkcs1SignatureFormatter or + this instanceof RsaPkcs1SignatureDeformatter + } +} + +class ByteArrayType extends Type { + ByteArrayType() { this.getName() = "Byte[]" } +} + +class ReadOnlyByteSpanType extends Type { + ReadOnlyByteSpanType() { this.getName() = "ReadOnlySpan" } +} + +class ByteArrayOrReadOnlyByteSpanType extends Type { + ByteArrayOrReadOnlyByteSpanType() { + this instanceof ByteArrayType or + this instanceof ReadOnlyByteSpanType + } +} + +class HashUse extends Crypto::AlgorithmValueConsumer instanceof MethodCall { + HashUse() { + this.getQualifier().getType() instanceof HashAlgorithmType and + this.getTarget() + .hasName([ + "ComputeHash", "ComputeHashAsync", "HashCore", "HashData", "HashDataAsync", + "TransformBlock", "TransformFinalBlock", "TryComputeHash", "TryHashData", + "TryHashFinal", "HashFinal" + ]) + } + + predicate isIntermediate() { super.getTarget().hasName("HashCore") } + + Expr getOutput() { + not this.isIntermediate() and + // some functions receive the destination as a parameter + if + super.getTarget().getName() = ["TryComputeHash", "TryHashFinal", "TryHashData"] + or + super.getTarget().getName() = ["HashData"] and super.getNumberOfArguments() = 2 + or + super.getTarget().getName() = ["HashDataAsync"] and super.getNumberOfArguments() = 3 + then result = super.getArgument(1) + else result = this + } + + Expr getInputArg() { + result = super.getArgument(0) and result.getType() instanceof ByteArrayOrReadOnlyByteSpanType + } + + Expr getStreamArg() { + result = super.getAnArgument() and + result.getType() instanceof Stream + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = super.getQualifier() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + Expr getQualifier() { result = super.getQualifier() } +} + +abstract class SignerQualifier extends Crypto::AlgorithmValueConsumer, SigningAlgorithmInstance instanceof Expr +{ + SignerQualifier() { this = any(SignerUse s).getQualifier() } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } +} + +class EcdsaSignerQualifier extends SignerQualifier instanceof Expr { + EcdsaSignerQualifier() { super.getType() instanceof EcdsaType } + + override string getRawAlgorithmName() { result = "ECDsa" } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::ECDSA()) + } +} + +class RsaSignerQualifier extends SignerQualifier instanceof Expr { + RsaSignerQualifier() { + super.getType() instanceof RsaType or super.getType() instanceof RsaPkcs1Type + } + + override string getRawAlgorithmName() { result = super.getType().getName() } + + override Crypto::KeyOpAlg::Algorithm getAlgorithmType() { + result = Crypto::KeyOpAlg::TSignature(Crypto::KeyOpAlg::OtherSignatureAlgorithmType()) + } +} + +class SignerUse extends MethodCall { + SignerUse() { + this.getTarget().getName().matches(["Verify%", "Sign%", "CreateSignature"]) and + this.getQualifier().getType() instanceof SignerType + } + + Expr getMessageArg() { + // Both Sign and Verify methods take the message as the first argument. + // Some cases the message is a hash. + result = this.getArgument(0) + } + + Expr getSignatureArg() { + this.isVerifier() and + ( + result = this.getArgument([1, 3]) and + result.getType() instanceof ByteArrayOrReadOnlyByteSpanType + ) + } + + predicate isIntermediate() { none() } + + Expr getSignatureOutput() { + this.isSigner() and + result = this + } + + Expr getHashAlgorithmArg() { + // Get the hash algorithm argument if it has the correct type. + result = this.getAnArgument() and result.getType() instanceof HashAlgorithmNameType + } + + predicate isSigner() { this.getTarget().getName().matches(["Sign%", "CreateSignature"]) } + + predicate isVerifier() { this.getTarget().getName().matches("Verify%") } +} + +/** + * An AEAD class, such as `AesGcm`, `AesCcm`, or `ChaCha20Poly1305`. + */ +class Aead extends Class { + Aead() { + this.hasFullyQualifiedName("System.Security.Cryptography", + ["AesGcm", "AesCcm", "ChaCha20Poly1305"]) + } +} + +class AeadCreation extends ObjectCreation { + AeadCreation() { this.getObjectType() instanceof Aead } + + Expr getKeyArg() { result = this.getArgument(0) } +} + +class AeadUse extends MethodCall { + AeadUse() { + this.getQualifier().getType() instanceof Aead and + this.getTarget().hasName(["Encrypt", "Decrypt"]) + } + + // One-shot API only. + predicate isIntermediate() { none() } + + Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if this.isEncrypt() + then result = Crypto::TEncryptMode() + else + if this.isDecrypt() + then result = Crypto::TDecryptMode() + else result = Crypto::TUnknownKeyOperationMode() + } + + predicate isEncrypt() { this.getTarget().getName() = "Encrypt" } + + predicate isDecrypt() { this.getTarget().getName() = "Decrypt" } + + Expr getNonceArg() { result = this.getArgument(0) } + + Expr getMessageArg() { result = this.getArgument(1) } + + Expr getOutputArg() { + this.isEncrypt() and + result = this.getArgument(2) + or + this.isDecrypt() and + result = this.getArgument(3) + } +} + +/** + * A symmetric algorithm class, such as AES or DES. + */ +class SymmetricAlgorithm extends Class { + SymmetricAlgorithm() { + this.getABaseType().hasFullyQualifiedName("System.Security.Cryptography", "SymmetricAlgorithm") + } + + CryptoTransformCreation getCreateTransformCall() { result = this.getAMethod().getACall() } +} + +/** + * A symmetric algorithm creation, such as `Aes.Create()`. + */ +class SymmetricAlgorithmCreation extends MethodCall { + SymmetricAlgorithmCreation() { + this.getTarget().hasName("Create") and + this.getQualifier().getType() instanceof SymmetricAlgorithm + } +} + +class SymmetricAlgorithmUse extends QualifiableExpr { + SymmetricAlgorithmUse() { + this.getQualifier().getType() instanceof SymmetricAlgorithm and + this.getQualifiedDeclaration() + .hasName([ + "EncryptCbc", "DecryptCbc", "EncryptCfb", "DecryptCfb", "EncryptEcb", "DecryptEcb", + "TryEncryptCbc", "TryDecryptCbc", "TryEncryptCfb", "TryDecryptCfb", "TryEncryptEcb", + "TryDecryptEcb", "CreateEncryptor", "CreateDecryptor", "Key", "IV", "Padding", "Mode" + ]) + } + + Expr getSymmetricAlgorithm() { result = this.getQualifier() } + + predicate isIntermediate() { + this.getQualifiedDeclaration().hasName(["Key", "IV", "Padding", "Mode"]) + } + + // The key may be set by assigning it to the `Key` property of the symmetric algorithm. + predicate isKeyConsumer() { + this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Key" + } + + // The IV may be set by assigning it to the `IV` property of the symmetric algorithm. + predicate isIvConsumer() { + this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "IV" + } + + // The padding mode may be set by assigning it to the `Padding` property of the symmetric algorithm. + predicate isPaddingConsumer() { + this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Padding" + } + + // The cipher mode may be set by assigning it to the `Mode` property of the symmetric algorithm. + predicate isModeConsumer() { + this instanceof PropertyWrite and this.getQualifiedDeclaration().getName() = "Mode" + } + + predicate isCreationCall() { this.getQualifiedDeclaration().getName().matches("Create%") } + + predicate isEncryptionCall() { + this.getQualifiedDeclaration().getName().matches(["Encrypt%", "TryEncrypt%"]) + } + + predicate isDecryptionCall() { + this.getQualifiedDeclaration().getName().matches(["Decrypt%", "TryDecrypt%"]) + } + + string getRawModeAlgorithmName() { + this.isEncryptionCall() and + result = this.getQualifiedDeclaration().getName().splitAt("Encrypt", 1) + or + this.isDecryptionCall() and + result = this.getQualifiedDeclaration().getName().splitAt("Decrypt", 1) + } + + Expr getInputArg() { + (this.isEncryptionCall() or this.isDecryptionCall()) and + result = this.(MethodCall).getArgument(0) + } + + Expr getIvArg() { + (this.isEncryptionCall() or this.isDecryptionCall()) and + this.getRawModeAlgorithmName().matches(["Cbc", "Cfb"]) and + result = this.(MethodCall).getArgument(1) + } + + Expr getPaddingArg() { + (this.isEncryptionCall() or this.isDecryptionCall()) and + result = this.(MethodCall).getArgument(this.(MethodCall).getNumberOfArguments() - 1) + } + + Expr getOutput() { + (this.isEncryptionCall() or this.isDecryptionCall()) and + result = this + } +} + +/** + * A call to `CreateEncryptor` or `CreateDecryptor` on a `SymmetricAlgorithm`. + */ +class CryptoTransformCreation extends MethodCall { + CryptoTransformCreation() { + this.getTarget().hasName(["CreateEncryptor", "CreateDecryptor"]) and + this.getQualifier().getType() instanceof SymmetricAlgorithm + } + + predicate isEncryptor() { this.getTarget().getName() = "CreateEncryptor" } + + predicate isDecryptor() { this.getTarget().getName() = "CreateDecryptor" } + + Expr getKeyArg() { result = this.getArgument(0) } + + Expr getIvArg() { result = this.getArgument(1) } + + SymmetricAlgorithm getSymmetricAlgorithm() { result = this.getQualifier().getType() } +} + +class CryptoStream extends Class { + CryptoStream() { this.hasFullyQualifiedName("System.Security.Cryptography", "CryptoStream") } +} + +class CryptoStreamMode extends MemberConstant { + CryptoStreamMode() { + this.getDeclaringType() + .hasFullyQualifiedName("System.Security.Cryptography", "CryptoStreamMode") + } + + predicate isRead() { this.getName() = "Read" } + + predicate isWrite() { this.getName() = "Write" } +} + +class PaddingMode extends MemberConstant { + PaddingMode() { + this.getDeclaringType().hasFullyQualifiedName("System.Security.Cryptography", "PaddingMode") + } +} + +class CipherMode extends MemberConstant { + CipherMode() { + this.getDeclaringType().hasFullyQualifiedName("System.Security.Cryptography", "CipherMode") + } +} + +class Stream extends Class { + Stream() { this.getABaseType().hasFullyQualifiedName("System.IO", "Stream") } +} + +/** + * A `Stream` object creation. + */ +class StreamCreation extends ObjectCreation { + StreamCreation() { this.getObjectType() instanceof Stream } + + Expr getInputArg() { + result = this.getAnArgument() and + result.getType().hasFullyQualifiedName("System", "Byte[]") + } + + Expr getStreamArg() { + result = this.getAnArgument() and + result.getType() instanceof Stream + } +} + +class StreamUse extends MethodCall { + StreamUse() { + this.getQualifier().getType() instanceof Stream and + this.getTarget().hasName(["ToArray", "Write"]) + } + + predicate isIntermediate() { this.getTarget().hasName("Write") } + + Expr getInputArg() { + this.isIntermediate() and + result = this.getArgument(0) + } + + Expr getOutput() { + not this.isIntermediate() and + result = this + } +} + +class CryptoStreamCreation extends ObjectCreation { + CryptoStreamCreation() { this.getObjectType() instanceof CryptoStream } + + Expr getStreamArg() { result = this.getArgument(0) } + + Expr getTransformArg() { result = this.getArgument(1) } + + Expr getModeArg() { result = this.getArgument(2) } + + Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if CryptoTransformFlow::getCreationFromUse(this.getTransformArg()).isEncryptor() + then result = Crypto::TEncryptMode() + else + if CryptoTransformFlow::getCreationFromUse(this.getTransformArg()).isDecryptor() + then result = Crypto::TDecryptMode() + else result = Crypto::TUnknownKeyOperationMode() + } +} + +class CryptoStreamUse extends MethodCall { + CryptoStreamUse() { + this.getQualifier().getType() instanceof CryptoStream and + this.getTarget().hasName(["Write", "FlushFinalBlock", "FlushFinalBlockAsync", "Close"]) + } + + predicate isIntermediate() { this.getTarget().getName() = "Write" } + + Expr getInputArg() { + this.isIntermediate() and + result = this.getArgument(0) + } +} + +class MacAlgorithmType extends CryptographyType { + MacAlgorithmType() { this.getName().matches(["HMAC%", "KeyedHashAlgorithm"]) } +} + +class HmacCreation extends ObjectCreation { + HmacCreation() { this.getObjectType() instanceof MacAlgorithmType } + + Expr getKeyArg() { if this.hasNoArguments() then result = this else result = this.getArgument(0) } + + string getRawAlgorithmName() { result = this.getObjectType().getName() } +} + +class MacUse extends Crypto::AlgorithmValueConsumer instanceof MethodCall { + MacUse() { + this.getQualifier().getType() instanceof MacAlgorithmType and + this.getTarget().hasName(["ComputeHash", "ComputeHashAsync", "HashData", "HashDataAsync"]) + } + + predicate isIntermediate() { none() } + + Expr getOutput() { + not this.isIntermediate() and + // some functions receive the destination as a parameter + if + super.getTarget().getName() = ["HashData"] and super.getNumberOfArguments() = 3 + or + super.getTarget().getName() = ["HashDataAsync"] and super.getNumberOfArguments() = 4 + then result = super.getArgument(2) + else result = this + } + + private Expr getDataArg() { + // ComputeHash and ComputeHashAsync take the data as the first argument. + if super.getTarget().getName().matches("ComputeHash%") + then result = super.getArgument(0) + else result = super.getArgument(1) + } + + Expr getInputArg() { + result = this.getDataArg() and result.getType() instanceof ByteArrayOrReadOnlyByteSpanType + } + + Expr getStreamArg() { result = this.getDataArg() and result.getType() instanceof Stream } + + Expr getKeyArg() { + if not super.getTarget().getName().matches("ComputeHash%") + then result = super.getArgument(0) + else result = HmacFlow::getCreationFromUse(this, _, _).getKeyArg() + } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = super.getQualifier() } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + Expr getQualifier() { result = super.getQualifier() } +} + +class HmacAlgorithmInstance extends Crypto::MACAlgorithmInstance instanceof Expr { + HmacAlgorithmInstance() { this = any(MacUse c).getQualifier() } + + override Crypto::TMACType getMACType() { result instanceof Crypto::THMAC } + + override string getRawMACAlgorithmName() { result = super.getType().getName() } +} + +class HmacAlgorithmQualifier extends Crypto::HMACAlgorithmInstance, Crypto::AlgorithmValueConsumer, + HmacAlgorithmInstance, Crypto::HashAlgorithmInstance instanceof Expr +{ + override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() { result = this } + + override Crypto::AlgorithmInstance getAKnownAlgorithmSource() { result = this } + + override Crypto::ConsumerInputDataFlowNode getInputNode() { none() } + + override Crypto::THashType getHashFamily() { + result = getHashFamily(this.getRawHashAlgorithmName()) + } + + override string getRawHashAlgorithmName() { + if super.getType().hasName("KeyedHashAlgorithm") + then result = this.getOriginalRawHashAlgorithmName() + else result = super.getType().getName().replaceAll("HMAC", "") + } + + override int getFixedDigestLength() { + hashAlgorithmToFamily(this.getRawHashAlgorithmName(), _, result) + } + + private string getOriginalRawHashAlgorithmName() { + exists(MacUse use | + use.getQualifier() = this and + result = HmacFlow::getCreationFromUse(use, _, _).getRawAlgorithmName().replaceAll("HMAC", "") + ) + } +} diff --git a/csharp/ql/lib/experimental/quantum/dotnet/FlowAnalysis.qll b/csharp/ql/lib/experimental/quantum/dotnet/FlowAnalysis.qll new file mode 100644 index 000000000000..a0c3a30994cd --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/dotnet/FlowAnalysis.qll @@ -0,0 +1,292 @@ +private import csharp +private import semmle.code.csharp.dataflow.DataFlow +private import experimental.quantum.Language +private import OperationInstances +private import AlgorithmValueConsumers +private import Cryptography + +signature class CreationCallSig instanceof Call; + +signature class UseCallSig instanceof QualifiableExpr { + predicate isIntermediate(); +} + +module CreationToUseFlow { + private module CreationToUseConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof Creation + or + exists(Use use | + source.asExpr() = use.(QualifiableExpr).getQualifier() and use.isIntermediate() + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(Use use | sink.asExpr() = use.(QualifiableExpr).getQualifier()) + } + } + + private module CreationToUseFlow = DataFlow::Global; + + Creation getCreationFromUse( + Use use, CreationToUseFlow::PathNode source, CreationToUseFlow::PathNode sink + ) { + source.getNode().asExpr() = result and + sink.getNode().asExpr() = use.(QualifiableExpr).getQualifier() and + CreationToUseFlow::flowPath(source, sink) + } + + Use getUseFromCreation( + Creation creation, CreationToUseFlow::PathNode source, CreationToUseFlow::PathNode sink + ) { + source.getNode().asExpr() = creation and + sink.getNode().asExpr() = result.(QualifiableExpr).getQualifier() and + CreationToUseFlow::flowPath(source, sink) + } + + Use getIntermediateUseFromUse( + Use use, CreationToUseFlow::PathNode source, CreationToUseFlow::PathNode sink + ) { + // Use sources are always intermediate uses. + source.getNode().asExpr() = result.(QualifiableExpr).getQualifier() and + sink.getNode().asExpr() = use.(QualifiableExpr).getQualifier() and + CreationToUseFlow::flowPath(source, sink) + } +} + +module HashAlgorithmNameToUseConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HashAlgorithmName } + + predicate isSink(DataFlow::Node sink) { + exists(HashAlgorithmNameConsumer consumer | sink = consumer.getInputNode()) + } +} + +module HashAlgorithmNameToUse = DataFlow::Global; + +module HashCreateToUseFlow = CreationToUseFlow; + +module CryptoStreamFlow = CreationToUseFlow; + +module AeadFlow = CreationToUseFlow; + +module HmacFlow = CreationToUseFlow; + +module SymmetricAlgorithmFlow = + CreationToUseFlow; + +/** + * A flow analysis module that tracks the flow from a `CryptoStreamMode.READ` or + * `CryptoStreamMode.WRITE` access to the corresponding `CryptoStream` object + * creation. + */ +module CryptoStreamModeFlow { + private module CryptoStreamModeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() = any(CryptoStreamMode mode).getAnAccess() + } + + predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(CryptoStreamCreation creation).getModeArg() + } + } + + private module CryptoStreamModeFlow = DataFlow::Global; + + CryptoStreamMode getModeFromCreation(CryptoStreamCreation creation) { + exists(CryptoStreamModeFlow::PathNode source, CryptoStreamModeFlow::PathNode sink | + source.getNode().asExpr() = result.getAnAccess() and + sink.getNode().asExpr() = creation.getAnArgument() and + CryptoStreamModeFlow::flowPath(source, sink) + ) + } +} + +/** + * A flow analysis module that tracks data flow from a `ICryptoTransform` + * creation (e.g. `Aes.CreateEncryptor()`) to the transform argument of a + * `CryptoStream` object creation. + */ +module CryptoTransformFlow { + private module CryptoTransformConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof CryptoTransformCreation } + + predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(ObjectCreation creation).getAnArgument() + } + } + + private module CryptoTransformFlow = DataFlow::Global; + + CryptoTransformCreation getCreationFromUse(ObjectCreation creation) { + exists(CryptoTransformFlow::PathNode source, CryptoTransformFlow::PathNode sink | + source.getNode().asExpr() = result and + sink.getNode().asExpr() = creation.getAnArgument() and + CryptoTransformFlow::flowPath(source, sink) + ) + } +} + +/** + * A flow analysis module that tracks the flow from a `PaddingMode` member + * access (e.g. `PaddingMode.PKCS7`) to a `Padding` property write on a + * `SymmetricAlgorithm` instance, or from a `CipherMode` member access + * (e.g. `CipherMode.CBC`) to a `Mode` property write on a `SymmetricAlgorithm` + * instance. + * + * Example: + * ``` + * Aes aes = Aes.Create(); + * aes.Padding = PaddingMode.PKCS7; + * ... + * ``` + */ +module ModeLiteralFlow { + private module ModeLiteralConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() = any(PaddingMode mode).getAnAccess() + or + source.asExpr() = any(CipherMode mode).getAnAccess() + } + + predicate isSink(DataFlow::Node sink) { + sink.asExpr() instanceof PaddingPropertyWrite or + sink.asExpr() instanceof PaddingModeArgument or + sink.asExpr() instanceof CipherModePropertyWrite + } + + // TODO: Figure out why this is needed. + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(Assignment assign | + node1.asExpr() = assign.getRValue() and + node2.asExpr() = assign.getLValue() + ) + } + } + + private module ModeLiteralFlow = DataFlow::Global; + + Expr getConsumer(Expr mode, ModeLiteralFlow::PathNode source, ModeLiteralFlow::PathNode sink) { + source.getNode().asExpr() = mode and + sink.getNode().asExpr() = result and + ModeLiteralFlow::flowPath(source, sink) + } +} + +/** + * A flow analysis module that tracks the flow from an arbitrary `Stream` object + * creation to the creation of a second `Stream` object wrapping the first one. + * + * This is useful for tracking the flow of data from a buffer passed to a + * `MemoryStream` to a `CryptoStream` wrapping the original `MemoryStream`. It + * can also be used to track dataflow from a `Stream` object to a call to + * `ToArray()` on the stream, or a wrapped stream. + */ +module StreamFlow { + private module StreamConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof StreamCreation + or + exists(StreamUse use | source.asExpr() = use.getQualifier()) + or + exists(Expr use | source.asExpr() = use and use.getType() instanceof Stream) + } + + predicate isSink(DataFlow::Node sink) { + sink.asExpr() instanceof StreamCreation + or + exists(StreamUse use | sink.asExpr() = use.getQualifier()) + or + exists(Expr use | sink.asExpr() = use and use.getType() instanceof Stream) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // Allow flow from one stream wrapped by a second stream. + exists(StreamCreation creation | + node1.asExpr() = creation.getStreamArg() and + node2.asExpr() = creation + ) + or + exists(MethodCall copy | + node1.asExpr() = copy.getQualifier() and + node2.asExpr() = copy.getAnArgument() and + copy.getTarget().hasName("CopyTo") + ) + } + } + + private module StreamFlow = DataFlow::Global; + + StreamCreation getWrappedStreamCreation( + StreamCreation stream, StreamFlow::PathNode source, StreamFlow::PathNode sink + ) { + source.getNode().asExpr() = result and + sink.getNode().asExpr() = stream and + StreamFlow::flowPath(source, sink) + } + + StreamUse getLaterUse(Expr use, StreamFlow::PathNode source, StreamFlow::PathNode sink) { + source.getNode().asExpr() = use and + sink.getNode().asExpr() = result.getQualifier() and + StreamFlow::flowPath(source, sink) + } + + StreamUse getEarlierUse(Expr use, StreamFlow::PathNode source, StreamFlow::PathNode sink) { + source.getNode().asExpr() = result.getQualifier() and + sink.getNode().asExpr() = use and + StreamFlow::flowPath(source, sink) + } +} + +/** + * An additional flow step across property assignments used to track flow from + * output artifacts to consumers. + * + * TODO: Figure out why this is needed. + */ +class PropertyWriteFlowStep extends AdditionalFlowInputStep { + Assignment assignment; + + PropertyWriteFlowStep() { + this.asExpr() = assignment.getRValue() and + assignment.getLValue() instanceof PropertyWrite + } + + override DataFlow::Node getOutput() { result.asExpr() = assignment.getLValue() } +} + +module SigningCreateToUseFlow { + private module SigningCreateToUseFlow implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SigningCreateCall } + + predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(SignerUse use).(QualifiableExpr).getQualifier() + } + + // Holds if the incoming node is an argument of the constructor call + // represented by the outgoing node. + // + // Example: + // ``` + // RSA rsa = RSA.Create() + // RSAPKCS1SignatureFormatter rsaFormatter = new(rsa); + // rsaFormatter.SetHashAlgorithm(nameof(SHA256)); + // signedHash = rsaFormatter.CreateSignature(hash); + // ``` + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(ObjectCreation create | + node2.asExpr() = create and node1.asExpr() = create.getAnArgument() + ) + } + } + + private module CreationToUseFlow = DataFlow::Global; + + SigningCreateCall getCreationFromUse( + SignerUse use, CreationToUseFlow::PathNode source, CreationToUseFlow::PathNode sink + ) { + source.getNode().asExpr() = result and + sink.getNode().asExpr() = use.(QualifiableExpr).getQualifier() and + CreationToUseFlow::flowPath(source, sink) + } +} diff --git a/csharp/ql/lib/experimental/quantum/dotnet/OperationInstances.qll b/csharp/ql/lib/experimental/quantum/dotnet/OperationInstances.qll new file mode 100644 index 000000000000..1a424f57a937 --- /dev/null +++ b/csharp/ql/lib/experimental/quantum/dotnet/OperationInstances.qll @@ -0,0 +1,228 @@ +private import csharp +private import DataFlow +private import experimental.quantum.Language +private import AlgorithmValueConsumers +private import FlowAnalysis +private import Cryptography + +class SigningOperationInstance extends Crypto::SignatureOperationInstance instanceof SignerUse { + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = super.getQualifier() + } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if super.isSigner() + then result = Crypto::TSignMode() + else + if super.isVerifier() + then result = Crypto::TVerifyMode() + else result = Crypto::TUnknownKeyOperationMode() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = SigningCreateToUseFlow::getCreationFromUse(this, _, _).getKeyConsumer() + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { none() } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = super.getMessageArg() + } + + override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { + result.asExpr() = super.getSignatureArg() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getSignatureOutput() + } +} + +class HashOperationInstance extends Crypto::HashOperationInstance instanceof HashUse { + HashOperationInstance() { not super.isIntermediate() } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getOutput() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = super.getInputArg() or + result.asExpr() = StreamFlow::getEarlierUse(super.getStreamArg(), _, _).getInputArg() + } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = super.getQualifier() + } +} + +/** + * A call to an encryption or decryption API (e.g. `EncryptCbc` or `EncryptCfb`) + * on a `SymmetricAlgorithm` instance. + */ +class SymmetricAlgorithmOperationInstance extends Crypto::KeyOperationInstance instanceof SymmetricAlgorithmUse +{ + SymmetricAlgorithmOperationInstance() { super.isEncryptionCall() or super.isDecryptionCall() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { result = this } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if super.isEncryptionCall() + then result = Crypto::TEncryptMode() + else + if super.isDecryptionCall() + then result = Crypto::TDecryptMode() + else result = Crypto::TUnknownKeyOperationMode() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = SymmetricAlgorithmFlow::getIntermediateUseFromUse(this, _, _) and + result.asExpr().(SymmetricAlgorithmUse).isKeyConsumer() + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + result.asExpr() = super.getIvArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = super.getInputArg() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getOutput() + } +} + +/** + * An instantiation of a `CryptoStream` object where the transform is a symmetric + * encryption or decryption operation (e.g. an encryption transform created by a + * call to `Aes.CreateEncryptor()`) + */ +class CryptoStreamOperationInstance extends Crypto::KeyOperationInstance instanceof CryptoStreamCreation +{ + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = this.getCryptoTransform() + } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + if this.getCryptoTransform().isEncryptor() + then result = Crypto::TEncryptMode() + else + if this.getCryptoTransform().isDecryptor() + then result = Crypto::TDecryptMode() + else result = Crypto::TUnknownKeyOperationMode() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + // If a key is explicitly provided as an argument when the transform is + // created, this takes precedence over any key that may be set on the + // symmetric algorithm instance. + if exists(this.getCryptoTransform().getKeyArg()) + then result.asExpr() = this.getCryptoTransform().getKeyArg() + else ( + result.asExpr() = + SymmetricAlgorithmFlow::getIntermediateUseFromUse(this.getCryptoTransform(), _, _) and + result.asExpr().(SymmetricAlgorithmUse).isKeyConsumer() + ) + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + // If an IV is explicitly provided as an argument when the transform is + // created, this takes precedence over any IV that may be set on the + // symmetric algorithm instance. + if exists(this.getCryptoTransform().getIvArg()) + then result.asExpr() = this.getCryptoTransform().getIvArg() + else ( + result.asExpr() = + SymmetricAlgorithmFlow::getIntermediateUseFromUse(this.getCryptoTransform(), _, _) and + result.asExpr().(SymmetricAlgorithmUse).isIvConsumer() + ) + } + + // Inputs can be passed to the `CryptoStream` instance in a number of ways. + // + // 1. Through the `stream` argument when the `CryptoStream` is created + // 2. Through calls to `Write()` on (a stream wrapped by) the stream argument + // 3. Through calls to write on this `CryptoStream` object + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = this.getWrappedStreamCreation().getInputArg() + or + result.asExpr() = this.getEarlierWrappedStreamUse().getInputArg() + or + result.asExpr() = CryptoStreamFlow::getUseFromCreation(this, _, _).getInputArg() + } + + // The output is obtained by calling `ToArray()` on a `Stream` either wrapped + // by the `CryptoStream` object, or copied from the `CryptoStream` object. + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + // We perform backwards dataflow to identify stream objects that are wrapped + // by the `CryptoStream` object, and then we look for calls to `ToArray()` + // on those streams. + result.asExpr() = this.getLaterWrappedStreamUse().getOutput() + } + + CryptoTransformCreation getCryptoTransform() { + result = CryptoTransformFlow::getCreationFromUse(this) and + (result.isEncryptor() or result.isDecryptor()) + } + + // Gets either this stream, or a stream wrapped by this stream. + StreamCreation getWrappedStreamCreation() { + result = StreamFlow::getWrappedStreamCreation(this, _, _) + } + + StreamUse getEarlierWrappedStreamUse() { + result = StreamFlow::getEarlierUse(this.getWrappedStreamCreation().getStreamArg(), _, _) + } + + StreamUse getLaterWrappedStreamUse() { + result = StreamFlow::getLaterUse(this.getWrappedStreamCreation().getStreamArg(), _, _) + } +} + +/** + * A call to either `Encrypt` or `Decrypt` on an `AesGcm`, `AesCcm`, or + * `ChaCha20Poly1305` instance. + */ +class AeadOperationInstance extends Crypto::KeyOperationInstance instanceof AeadUse { + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + // See `AeadModeAlgorithmValueConsumer` for the algorithm value consumer. + result = this + } + + override Crypto::KeyOperationSubtype getKeyOperationSubtype() { + result = this.(AeadUse).getKeyOperationSubtype() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = AeadFlow::getCreationFromUse(this, _, _).getKeyArg() + } + + override Crypto::ConsumerInputDataFlowNode getNonceConsumer() { + result.asExpr() = super.getNonceArg() + } + + override Crypto::ConsumerInputDataFlowNode getInputConsumer() { + result.asExpr() = super.getMessageArg() + } + + override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() { + result.asExpr() = super.getOutputArg() + } +} + +class HmacOperationInstance extends Crypto::MACOperationInstance instanceof MacUse { + HmacOperationInstance() { not super.isIntermediate() } + + override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() { + result = super.getQualifier() + } + + override Crypto::ConsumerInputDataFlowNode getMessageConsumer() { + result.asExpr() = super.getInputArg() or + result.asExpr() = StreamFlow::getEarlierUse(super.getStreamArg(), _, _).getInputArg() + } + + override Crypto::ConsumerInputDataFlowNode getKeyConsumer() { + result.asExpr() = super.getKeyArg() + } +} diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 464284c56cb4..17298206902c 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -9,6 +9,7 @@ dependencies: codeql/controlflow: ${workspace} codeql/dataflow: ${workspace} codeql/mad: ${workspace} + codeql/quantum: ${workspace} codeql/ssa: ${workspace} codeql/threat-models: ${workspace} codeql/tutorial: ${workspace} diff --git a/csharp/ql/src/experimental/quantum/PrintCBOMGraph.ql b/csharp/ql/src/experimental/quantum/PrintCBOMGraph.ql new file mode 100644 index 000000000000..4dd33d070c07 --- /dev/null +++ b/csharp/ql/src/experimental/quantum/PrintCBOMGraph.ql @@ -0,0 +1,23 @@ +/** + * @name Print CBOM Graph + * @description Outputs a graph representation of the cryptographic bill of materials. + * This query only supports DGML output, as CodeQL DOT output omits properties. + * @kind graph + * @id csharp/print-cbom-graph + * @tags quantum + * experimental + */ + +import experimental.quantum.Language + +query predicate nodes(Crypto::NodeBase node, string key, string value) { + Crypto::nodes_graph_impl(node, key, value) +} + +query predicate edges(Crypto::NodeBase source, Crypto::NodeBase target, string key, string value) { + Crypto::edges_graph_impl(source, target, key, value) +} + +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "graph" +} diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesCbcExample.cs b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesCbcExample.cs new file mode 100644 index 000000000000..4cdaf439932f --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesCbcExample.cs @@ -0,0 +1,91 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace QuantumExamples.Cryptography +{ + public class AesCbcExample + { + public static void RunExample() + { + const string originalMessage = "This is a secret message!"; + + byte[] key = GenerateRandomKey(); + byte[] iv = GenerateRandomIV(); + + byte[] encryptedData = EncryptStringWithCbc(originalMessage, key, iv); + string decryptedMessage = DecryptStringWithCbc(encryptedData, key, iv); + + bool isSuccessful = originalMessage == decryptedMessage; + Console.WriteLine("Decryption successful: {0}", isSuccessful); + } + + private static byte[] EncryptStringWithCbc(string plaintext, byte[] key, byte[] iv) + { + if (plaintext == null) + throw new ArgumentNullException(nameof(plaintext)); + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (iv == null) + throw new ArgumentNullException(nameof(iv)); + + try + { + using (Aes aes = Aes.Create()) + { + aes.Key = key; + byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext); + return aes.EncryptCbc(plaintextBytes, iv, PaddingMode.PKCS7); + } + } + catch (CryptographicException ex) + { + throw new CryptographicException("Encryption failed.", ex); + } + } + + private static string DecryptStringWithCbc(byte[] ciphertext, byte[] key, byte[] iv) + { + if (ciphertext == null) + throw new ArgumentNullException(nameof(ciphertext)); + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (iv == null) + throw new ArgumentNullException(nameof(iv)); + + try + { + using (Aes aes = Aes.Create()) + { + aes.Key = key; + byte[] decryptedBytes = aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); + return Encoding.UTF8.GetString(decryptedBytes); + } + } + catch (CryptographicException ex) + { + throw new CryptographicException("Decryption failed.", ex); + } + } + + private static byte[] GenerateRandomKey() + { + byte[] key = new byte[32]; // 256-bit key + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(key); + } + return key; + } + + private static byte[] GenerateRandomIV() + { + byte[] iv = new byte[16]; // 128-bit IV + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(iv); + } + return iv; + } + } +} diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesCfbExample.cs b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesCfbExample.cs new file mode 100644 index 000000000000..3e510cf8597a --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesCfbExample.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace QuantumExamples.Cryptography +{ + public class AesCfbExample + { + public static void RunExample() + { + const string originalMessage = "This is a secret message!"; + + byte[] key = GenerateRandomKey(); + byte[] iv = GenerateRandomIV(); + + byte[] encryptedData = EncryptStringWithCfb(originalMessage, key, iv); + string decryptedMessage = DecryptStringWithCfb(encryptedData, key, iv); + + bool isSuccessful = originalMessage == decryptedMessage; + Console.WriteLine("Decryption successful: {0}", isSuccessful); + } + + private static byte[] EncryptStringWithCfb(string plainText, byte[] key, byte[] iv) + { + byte[] encrypted; + + using (Aes aes = Aes.Create()) + { + // Set the key and IV on the AES instance. + aes.Key = key; + aes.IV = iv; + aes.Mode = CipherMode.CFB; + aes.Padding = PaddingMode.None; + + ICryptoTransform encryptor = aes.CreateEncryptor(); + byte[] plainBytes = Encoding.UTF8.GetBytes(plainText); + + // Create an empty memory stream and write the plaintext to the crypto stream. + using (MemoryStream msEncrypt = new MemoryStream()) + { + using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) + { + csEncrypt.Write(plainBytes, 0, plainBytes.Length); + csEncrypt.FlushFinalBlock(); + } + encrypted = msEncrypt.ToArray(); + } + } + return encrypted; + } + + private static string DecryptStringWithCfb(byte[] cipherText, byte[] key, byte[] iv) + { + string decrypted; + + using (Aes aes = Aes.Create()) + { + aes.Mode = CipherMode.CFB; + aes.Padding = PaddingMode.None; + + // Pass the key and IV to the decryptor directly. + ICryptoTransform decryptor = aes.CreateDecryptor(key, iv); + + // Pass the ciphertext to the memory stream directly. + using (MemoryStream msDecrypt = new MemoryStream(cipherText)) + { + using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (MemoryStream msPlain = new MemoryStream()) + { + csDecrypt.CopyTo(msPlain); + byte[] plainBytes = msPlain.ToArray(); + decrypted = Encoding.UTF8.GetString(plainBytes); + } + } + } + } + return decrypted; + } + + private static byte[] GenerateRandomKey() + { + byte[] key = new byte[32]; // 256-bit key + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(key); + } + return key; + } + + private static byte[] GenerateRandomIV() + { + byte[] iv = new byte[16]; // 128-bit IV + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(iv); + } + return iv; + } + } +} diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesGcmExample.cs b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesGcmExample.cs new file mode 100644 index 000000000000..5f5ec58e4f58 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/AesGcmExample.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace QuantumExamples.Cryptography +{ + public class AesGcmExample + { + public static void RunExample() + { + const string originalMessage = "This is a secret message!"; + + byte[] key = GenerateRandomKey(); + byte[] nonce = GenerateRandomNonce(); + + var (encryptedMessage, tag) = EncryptStringWithGcm(originalMessage, key, nonce); + string decryptedMessage = DecryptStringWithGcm(encryptedMessage, key, nonce, tag); + + bool isSuccessful = originalMessage == decryptedMessage; + Console.WriteLine("Decryption successful: {0}", isSuccessful); + } + + private static (byte[], byte[]) EncryptStringWithGcm(string plaintext, byte[] key, byte[] nonce) + { + using (var aes = new AesGcm(key, AesGcm.TagByteSizes.MaxSize)) + { + var plaintextBytes = Encoding.UTF8.GetBytes(plaintext); + var ciphertext = new byte[plaintextBytes.Length]; + var tag = new byte[AesGcm.TagByteSizes.MaxSize]; + aes.Encrypt(nonce, plaintextBytes, ciphertext, tag); + + return (ciphertext, tag); + } + } + + private static string DecryptStringWithGcm(byte[] ciphertext, byte[] key, byte[] nonce, byte[] tag) + { + using (var aes = new AesGcm(key, AesGcm.TagByteSizes.MaxSize)) + { + var plaintextBytes = new byte[ciphertext.Length]; + aes.Decrypt(nonce, ciphertext, tag, plaintextBytes); + + return Encoding.UTF8.GetString(plaintextBytes); + } + } + + private static byte[] GenerateRandomKey() + { + byte[] key = new byte[32]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(key); + } + return key; + } + + private static byte[] GenerateRandomNonce() + { + byte[] nonce = new byte[AesGcm.NonceByteSizes.MaxSize]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(nonce); + } + return nonce; + } + } +} diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_edges.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_edges.expected new file mode 100644 index 000000000000..a31125579a0f --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_edges.expected @@ -0,0 +1,60 @@ +| AesCbcExample.cs:36:21:36:27 | Key | Source | AesCbcExample.cs:76:30:76:32 | RandomNumberGeneration | +| AesCbcExample.cs:38:28:38:80 | EncryptOperation | Algorithm | AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | +| AesCbcExample.cs:38:28:38:80 | EncryptOperation | Input | AesCbcExample.cs:38:43:38:56 | Message | +| AesCbcExample.cs:38:28:38:80 | EncryptOperation | Key | AesCbcExample.cs:36:21:36:27 | Key | +| AesCbcExample.cs:38:28:38:80 | EncryptOperation | Nonce | AesCbcExample.cs:38:59:38:60 | Nonce | +| AesCbcExample.cs:38:28:38:80 | EncryptOperation | Output | AesCbcExample.cs:38:28:38:80 | KeyOperationOutput | +| AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | Mode | AesCbcExample.cs:38:28:38:80 | ModeOfOperation | +| AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | Padding | AesCbcExample.cs:38:63:38:79 | PaddingAlgorithm | +| AesCbcExample.cs:38:43:38:56 | Message | Source | AesCbcExample.cs:38:43:38:56 | Message | +| AesCbcExample.cs:38:59:38:60 | Nonce | Source | AesCbcExample.cs:86:30:86:31 | RandomNumberGeneration | +| AesCbcExample.cs:60:21:60:27 | Key | Source | AesCbcExample.cs:76:30:76:32 | RandomNumberGeneration | +| AesCbcExample.cs:61:45:61:93 | DecryptOperation | Algorithm | AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | +| AesCbcExample.cs:61:45:61:93 | DecryptOperation | Input | AesCbcExample.cs:61:60:61:69 | Message | +| AesCbcExample.cs:61:45:61:93 | DecryptOperation | Key | AesCbcExample.cs:60:21:60:27 | Key | +| AesCbcExample.cs:61:45:61:93 | DecryptOperation | Nonce | AesCbcExample.cs:61:72:61:73 | Nonce | +| AesCbcExample.cs:61:45:61:93 | DecryptOperation | Output | AesCbcExample.cs:61:45:61:93 | KeyOperationOutput | +| AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | Mode | AesCbcExample.cs:61:45:61:93 | ModeOfOperation | +| AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | Padding | AesCbcExample.cs:61:76:61:92 | PaddingAlgorithm | +| AesCbcExample.cs:61:60:61:69 | Message | Source | AesCbcExample.cs:38:28:38:80 | KeyOperationOutput | +| AesCbcExample.cs:61:72:61:73 | Nonce | Source | AesCbcExample.cs:86:30:86:31 | RandomNumberGeneration | +| AesCfbExample.cs:31:17:31:23 | Key | Source | AesCfbExample.cs:87:30:87:32 | RandomNumberGeneration | +| AesCfbExample.cs:32:17:32:22 | Nonce | Source | AesCfbExample.cs:97:30:97:31 | RandomNumberGeneration | +| AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | Mode | AesCfbExample.cs:33:28:33:41 | ModeOfOperation | +| AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | Padding | AesCfbExample.cs:34:31:34:46 | PaddingAlgorithm | +| AesCfbExample.cs:42:53:42:114 | EncryptOperation | Algorithm | AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | +| AesCfbExample.cs:42:53:42:114 | EncryptOperation | Input | AesCfbExample.cs:44:41:44:50 | Message | +| AesCfbExample.cs:42:53:42:114 | EncryptOperation | Key | AesCfbExample.cs:31:17:31:23 | Key | +| AesCfbExample.cs:42:53:42:114 | EncryptOperation | Nonce | AesCfbExample.cs:32:17:32:22 | Nonce | +| AesCfbExample.cs:42:53:42:114 | EncryptOperation | Output | AesCfbExample.cs:47:33:47:51 | KeyOperationOutput | +| AesCfbExample.cs:44:41:44:50 | Message | Source | AesCfbExample.cs:44:41:44:50 | Message | +| AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | Mode | AesCfbExample.cs:59:28:59:41 | ModeOfOperation | +| AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | Padding | AesCfbExample.cs:60:31:60:46 | PaddingAlgorithm | +| AesCfbExample.cs:63:66:63:68 | Key | Source | AesCfbExample.cs:87:30:87:32 | RandomNumberGeneration | +| AesCfbExample.cs:63:71:63:72 | Nonce | Source | AesCfbExample.cs:97:30:97:31 | RandomNumberGeneration | +| AesCfbExample.cs:66:66:66:75 | Message | Source | AesCfbExample.cs:47:33:47:51 | KeyOperationOutput | +| AesCfbExample.cs:68:53:68:113 | DecryptOperation | Algorithm | AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | +| AesCfbExample.cs:68:53:68:113 | DecryptOperation | Input | AesCfbExample.cs:66:66:66:75 | Message | +| AesCfbExample.cs:68:53:68:113 | DecryptOperation | Key | AesCfbExample.cs:63:66:63:68 | Key | +| AesCfbExample.cs:68:53:68:113 | DecryptOperation | Nonce | AesCfbExample.cs:63:71:63:72 | Nonce | +| AesCfbExample.cs:68:53:68:113 | DecryptOperation | Output | AesCfbExample.cs:73:49:73:65 | KeyOperationOutput | +| AesGcmExample.cs:26:41:26:43 | Key | Source | AesGcmExample.cs:53:30:53:32 | RandomNumberGeneration | +| AesGcmExample.cs:31:17:31:67 | EncryptOperation | Algorithm | AesGcmExample.cs:31:17:31:67 | KeyOperationAlgorithm | +| AesGcmExample.cs:31:17:31:67 | EncryptOperation | Input | AesGcmExample.cs:31:36:31:49 | Message | +| AesGcmExample.cs:31:17:31:67 | EncryptOperation | Key | AesGcmExample.cs:26:41:26:43 | Key | +| AesGcmExample.cs:31:17:31:67 | EncryptOperation | Nonce | AesGcmExample.cs:31:29:31:33 | Nonce | +| AesGcmExample.cs:31:17:31:67 | EncryptOperation | Output | AesGcmExample.cs:31:52:31:61 | KeyOperationOutput | +| AesGcmExample.cs:31:17:31:67 | KeyOperationAlgorithm | Mode | AesGcmExample.cs:31:17:31:67 | ModeOfOperation | +| AesGcmExample.cs:31:17:31:67 | KeyOperationAlgorithm | Padding | AesGcmExample.cs:31:17:31:67 | KeyOperationAlgorithm | +| AesGcmExample.cs:31:29:31:33 | Nonce | Source | AesGcmExample.cs:63:30:63:34 | RandomNumberGeneration | +| AesGcmExample.cs:31:36:31:49 | Message | Source | AesGcmExample.cs:31:36:31:49 | Message | +| AesGcmExample.cs:39:41:39:43 | Key | Source | AesGcmExample.cs:53:30:53:32 | RandomNumberGeneration | +| AesGcmExample.cs:42:17:42:67 | DecryptOperation | Algorithm | AesGcmExample.cs:42:17:42:67 | KeyOperationAlgorithm | +| AesGcmExample.cs:42:17:42:67 | DecryptOperation | Input | AesGcmExample.cs:42:36:42:45 | Message | +| AesGcmExample.cs:42:17:42:67 | DecryptOperation | Key | AesGcmExample.cs:39:41:39:43 | Key | +| AesGcmExample.cs:42:17:42:67 | DecryptOperation | Nonce | AesGcmExample.cs:42:29:42:33 | Nonce | +| AesGcmExample.cs:42:17:42:67 | DecryptOperation | Output | AesGcmExample.cs:42:53:42:66 | KeyOperationOutput | +| AesGcmExample.cs:42:17:42:67 | KeyOperationAlgorithm | Mode | AesGcmExample.cs:42:17:42:67 | ModeOfOperation | +| AesGcmExample.cs:42:17:42:67 | KeyOperationAlgorithm | Padding | AesGcmExample.cs:42:17:42:67 | KeyOperationAlgorithm | +| AesGcmExample.cs:42:29:42:33 | Nonce | Source | AesGcmExample.cs:63:30:63:34 | RandomNumberGeneration | +| AesGcmExample.cs:42:36:42:45 | Message | Source | AesGcmExample.cs:31:52:31:61 | KeyOperationOutput | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_edges.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_edges.ql new file mode 100644 index 000000000000..b793c5b7480f --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_edges.ql @@ -0,0 +1,5 @@ +import csharp +import experimental.quantum.Language + +from Crypto::NodeBase n, string key +select n, key, n.getChild(key) diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_properties.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_properties.expected new file mode 100644 index 000000000000..64c923b7ad98 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_properties.expected @@ -0,0 +1,44 @@ +| AesCbcExample.cs:36:21:36:27 | Key | KeyType | Unknown | AesCbcExample.cs:36:21:36:27 | AesCbcExample.cs:36:21:36:27 | +| AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | Name | AES | AesCbcExample.cs:38:28:38:80 | AesCbcExample.cs:38:28:38:80 | +| AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | RawName | Aes | AesCbcExample.cs:38:28:38:80 | AesCbcExample.cs:38:28:38:80 | +| AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | Structure | Block | AesCbcExample.cs:38:28:38:80 | AesCbcExample.cs:38:28:38:80 | +| AesCbcExample.cs:38:28:38:80 | ModeOfOperation | Name | CBC | AesCbcExample.cs:38:28:38:80 | AesCbcExample.cs:38:28:38:80 | +| AesCbcExample.cs:38:28:38:80 | ModeOfOperation | RawName | Cbc | AesCbcExample.cs:38:28:38:80 | AesCbcExample.cs:38:28:38:80 | +| AesCbcExample.cs:38:63:38:79 | PaddingAlgorithm | Name | PKCS7 | AesCbcExample.cs:38:63:38:79 | AesCbcExample.cs:38:63:38:79 | +| AesCbcExample.cs:38:63:38:79 | PaddingAlgorithm | RawName | PKCS7 | AesCbcExample.cs:38:63:38:79 | AesCbcExample.cs:38:63:38:79 | +| AesCbcExample.cs:60:21:60:27 | Key | KeyType | Unknown | AesCbcExample.cs:60:21:60:27 | AesCbcExample.cs:60:21:60:27 | +| AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | Name | AES | AesCbcExample.cs:61:45:61:93 | AesCbcExample.cs:61:45:61:93 | +| AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | RawName | Aes | AesCbcExample.cs:61:45:61:93 | AesCbcExample.cs:61:45:61:93 | +| AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | Structure | Block | AesCbcExample.cs:61:45:61:93 | AesCbcExample.cs:61:45:61:93 | +| AesCbcExample.cs:61:45:61:93 | ModeOfOperation | Name | CBC | AesCbcExample.cs:61:45:61:93 | AesCbcExample.cs:61:45:61:93 | +| AesCbcExample.cs:61:45:61:93 | ModeOfOperation | RawName | Cbc | AesCbcExample.cs:61:45:61:93 | AesCbcExample.cs:61:45:61:93 | +| AesCbcExample.cs:61:76:61:92 | PaddingAlgorithm | Name | PKCS7 | AesCbcExample.cs:61:76:61:92 | AesCbcExample.cs:61:76:61:92 | +| AesCbcExample.cs:61:76:61:92 | PaddingAlgorithm | RawName | PKCS7 | AesCbcExample.cs:61:76:61:92 | AesCbcExample.cs:61:76:61:92 | +| AesCbcExample.cs:76:30:76:32 | RandomNumberGeneration | Description | RandomNumberGenerator | AesCbcExample.cs:76:30:76:32 | AesCbcExample.cs:76:30:76:32 | +| AesCbcExample.cs:86:30:86:31 | RandomNumberGeneration | Description | RandomNumberGenerator | AesCbcExample.cs:86:30:86:31 | AesCbcExample.cs:86:30:86:31 | +| AesCfbExample.cs:31:17:31:23 | Key | KeyType | Unknown | AesCfbExample.cs:31:17:31:23 | AesCfbExample.cs:31:17:31:23 | +| AesCfbExample.cs:33:28:33:41 | ModeOfOperation | Name | CFB | AesCfbExample.cs:33:28:33:41 | AesCfbExample.cs:33:28:33:41 | +| AesCfbExample.cs:33:28:33:41 | ModeOfOperation | RawName | CFB | AesCfbExample.cs:33:28:33:41 | AesCfbExample.cs:33:28:33:41 | +| AesCfbExample.cs:34:31:34:46 | PaddingAlgorithm | Name | NoPadding | AesCfbExample.cs:34:31:34:46 | AesCfbExample.cs:34:31:34:46 | +| AesCfbExample.cs:34:31:34:46 | PaddingAlgorithm | RawName | None | AesCfbExample.cs:34:31:34:46 | AesCfbExample.cs:34:31:34:46 | +| AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | Name | AES | AesCfbExample.cs:36:46:36:66 | AesCfbExample.cs:36:46:36:66 | +| AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | RawName | Aes | AesCfbExample.cs:36:46:36:66 | AesCfbExample.cs:36:46:36:66 | +| AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | Structure | Block | AesCfbExample.cs:36:46:36:66 | AesCfbExample.cs:36:46:36:66 | +| AesCfbExample.cs:59:28:59:41 | ModeOfOperation | Name | CFB | AesCfbExample.cs:59:28:59:41 | AesCfbExample.cs:59:28:59:41 | +| AesCfbExample.cs:59:28:59:41 | ModeOfOperation | RawName | CFB | AesCfbExample.cs:59:28:59:41 | AesCfbExample.cs:59:28:59:41 | +| AesCfbExample.cs:60:31:60:46 | PaddingAlgorithm | Name | NoPadding | AesCfbExample.cs:60:31:60:46 | AesCfbExample.cs:60:31:60:46 | +| AesCfbExample.cs:60:31:60:46 | PaddingAlgorithm | RawName | None | AesCfbExample.cs:60:31:60:46 | AesCfbExample.cs:60:31:60:46 | +| AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | Name | AES | AesCfbExample.cs:63:46:63:73 | AesCfbExample.cs:63:46:63:73 | +| AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | RawName | Aes | AesCfbExample.cs:63:46:63:73 | AesCfbExample.cs:63:46:63:73 | +| AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | Structure | Block | AesCfbExample.cs:63:46:63:73 | AesCfbExample.cs:63:46:63:73 | +| AesCfbExample.cs:63:66:63:68 | Key | KeyType | Unknown | AesCfbExample.cs:63:66:63:68 | AesCfbExample.cs:63:66:63:68 | +| AesCfbExample.cs:87:30:87:32 | RandomNumberGeneration | Description | RandomNumberGenerator | AesCfbExample.cs:87:30:87:32 | AesCfbExample.cs:87:30:87:32 | +| AesCfbExample.cs:97:30:97:31 | RandomNumberGeneration | Description | RandomNumberGenerator | AesCfbExample.cs:97:30:97:31 | AesCfbExample.cs:97:30:97:31 | +| AesGcmExample.cs:26:41:26:43 | Key | KeyType | Unknown | AesGcmExample.cs:26:41:26:43 | AesGcmExample.cs:26:41:26:43 | +| AesGcmExample.cs:31:17:31:67 | ModeOfOperation | Name | GCM | AesGcmExample.cs:31:17:31:67 | AesGcmExample.cs:31:17:31:67 | +| AesGcmExample.cs:31:17:31:67 | ModeOfOperation | RawName | Gcm | AesGcmExample.cs:31:17:31:67 | AesGcmExample.cs:31:17:31:67 | +| AesGcmExample.cs:39:41:39:43 | Key | KeyType | Unknown | AesGcmExample.cs:39:41:39:43 | AesGcmExample.cs:39:41:39:43 | +| AesGcmExample.cs:42:17:42:67 | ModeOfOperation | Name | GCM | AesGcmExample.cs:42:17:42:67 | AesGcmExample.cs:42:17:42:67 | +| AesGcmExample.cs:42:17:42:67 | ModeOfOperation | RawName | Gcm | AesGcmExample.cs:42:17:42:67 | AesGcmExample.cs:42:17:42:67 | +| AesGcmExample.cs:53:30:53:32 | RandomNumberGeneration | Description | RandomNumberGenerator | AesGcmExample.cs:53:30:53:32 | AesGcmExample.cs:53:30:53:32 | +| AesGcmExample.cs:63:30:63:34 | RandomNumberGeneration | Description | RandomNumberGenerator | AesGcmExample.cs:63:30:63:34 | AesGcmExample.cs:63:30:63:34 | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_properties.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_properties.ql new file mode 100644 index 000000000000..322758d018be --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/node_properties.ql @@ -0,0 +1,6 @@ +import csharp +import experimental.quantum.Language + +from Crypto::NodeBase n, string key, string value, Location location +where n.properties(key, value, location) +select n, key, value, location diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/nodes.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/nodes.expected new file mode 100644 index 000000000000..1be4aa229b28 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/nodes.expected @@ -0,0 +1,52 @@ +| AesCbcExample.cs:36:21:36:27 | Key | +| AesCbcExample.cs:38:28:38:80 | EncryptOperation | +| AesCbcExample.cs:38:28:38:80 | KeyOperationAlgorithm | +| AesCbcExample.cs:38:28:38:80 | KeyOperationOutput | +| AesCbcExample.cs:38:28:38:80 | ModeOfOperation | +| AesCbcExample.cs:38:43:38:56 | Message | +| AesCbcExample.cs:38:59:38:60 | Nonce | +| AesCbcExample.cs:38:63:38:79 | PaddingAlgorithm | +| AesCbcExample.cs:60:21:60:27 | Key | +| AesCbcExample.cs:61:45:61:93 | DecryptOperation | +| AesCbcExample.cs:61:45:61:93 | KeyOperationAlgorithm | +| AesCbcExample.cs:61:45:61:93 | KeyOperationOutput | +| AesCbcExample.cs:61:45:61:93 | ModeOfOperation | +| AesCbcExample.cs:61:60:61:69 | Message | +| AesCbcExample.cs:61:72:61:73 | Nonce | +| AesCbcExample.cs:61:76:61:92 | PaddingAlgorithm | +| AesCbcExample.cs:76:30:76:32 | RandomNumberGeneration | +| AesCbcExample.cs:86:30:86:31 | RandomNumberGeneration | +| AesCfbExample.cs:31:17:31:23 | Key | +| AesCfbExample.cs:32:17:32:22 | Nonce | +| AesCfbExample.cs:33:28:33:41 | ModeOfOperation | +| AesCfbExample.cs:34:31:34:46 | PaddingAlgorithm | +| AesCfbExample.cs:36:46:36:66 | KeyOperationAlgorithm | +| AesCfbExample.cs:42:53:42:114 | EncryptOperation | +| AesCfbExample.cs:44:41:44:50 | Message | +| AesCfbExample.cs:47:33:47:51 | KeyOperationOutput | +| AesCfbExample.cs:59:28:59:41 | ModeOfOperation | +| AesCfbExample.cs:60:31:60:46 | PaddingAlgorithm | +| AesCfbExample.cs:63:46:63:73 | KeyOperationAlgorithm | +| AesCfbExample.cs:63:66:63:68 | Key | +| AesCfbExample.cs:63:71:63:72 | Nonce | +| AesCfbExample.cs:66:66:66:75 | Message | +| AesCfbExample.cs:68:53:68:113 | DecryptOperation | +| AesCfbExample.cs:73:49:73:65 | KeyOperationOutput | +| AesCfbExample.cs:87:30:87:32 | RandomNumberGeneration | +| AesCfbExample.cs:97:30:97:31 | RandomNumberGeneration | +| AesGcmExample.cs:26:41:26:43 | Key | +| AesGcmExample.cs:31:17:31:67 | EncryptOperation | +| AesGcmExample.cs:31:17:31:67 | KeyOperationAlgorithm | +| AesGcmExample.cs:31:17:31:67 | ModeOfOperation | +| AesGcmExample.cs:31:29:31:33 | Nonce | +| AesGcmExample.cs:31:36:31:49 | Message | +| AesGcmExample.cs:31:52:31:61 | KeyOperationOutput | +| AesGcmExample.cs:39:41:39:43 | Key | +| AesGcmExample.cs:42:17:42:67 | DecryptOperation | +| AesGcmExample.cs:42:17:42:67 | KeyOperationAlgorithm | +| AesGcmExample.cs:42:17:42:67 | ModeOfOperation | +| AesGcmExample.cs:42:29:42:33 | Nonce | +| AesGcmExample.cs:42:36:42:45 | Message | +| AesGcmExample.cs:42:53:42:66 | KeyOperationOutput | +| AesGcmExample.cs:53:30:53:32 | RandomNumberGeneration | +| AesGcmExample.cs:63:30:63:34 | RandomNumberGeneration | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/nodes.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/nodes.ql new file mode 100644 index 000000000000..d17b30ab08cd --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/nodes.ql @@ -0,0 +1,5 @@ +import csharp +import experimental.quantum.Language + +from Crypto::NodeBase n +select n diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/options b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/options new file mode 100644 index 000000000000..f4586e95ef0c --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/ciphers/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/HashExample.cs b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/HashExample.cs new file mode 100644 index 000000000000..8edd033abbb5 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/HashExample.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace QuantumExamples.Cryptography +{ + public class HashExample + { + public static void RunExample() + { + const string originalMessage = "This is a message to hash!"; + + // Demonstrate various hash algorithms + DemonstrateBasicHashing(originalMessage); + DemonstrateStreamHashing(originalMessage); + DemonstrateOneShotHashing(originalMessage); + } + + private static void DemonstrateBasicHashing(string message) + { + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + + using (SHA256 sha256 = SHA256.Create()) + { + byte[] hash = sha256.ComputeHash(messageBytes); + Console.WriteLine("SHA256 hash: {0}", Convert.ToBase64String(hash)); + } + + using (SHA1 sha1 = SHA1.Create()) + { + byte[] hash = sha1.ComputeHash(messageBytes); + Console.WriteLine("SHA1 hash: {0}", Convert.ToBase64String(hash)); + } + + using (MD5 md5 = MD5.Create()) + { + byte[] hash = md5.ComputeHash(messageBytes); + Console.WriteLine("MD5 hash: {0}", Convert.ToBase64String(hash)); + } + } + + private static void DemonstrateStreamHashing(string message) + { + using SHA256 sha256 = SHA256.Create(); + using MemoryStream stream = new MemoryStream(); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + stream.Write(messageBytes, 0, messageBytes.Length); + stream.Position = 0; + + byte[] hash = sha256.ComputeHash(stream); + Console.WriteLine("Stream-based SHA256 hash: {0}", Convert.ToBase64String(hash)); + } + + private static void DemonstrateOneShotHashing(string message) + { + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + + byte[] sha256Hash = SHA256.HashData(messageBytes); + Console.WriteLine("One-shot SHA256 hash: {0}", Convert.ToBase64String(sha256Hash)); + + byte[] sha1Hash = SHA1.HashData(messageBytes); + Console.WriteLine("One-shot SHA1 hash: {0}", Convert.ToBase64String(sha1Hash)); + + byte[] md5Hash = MD5.HashData(messageBytes); + Console.WriteLine("One-shot MD5 hash: {0}", Convert.ToBase64String(md5Hash)); + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/hash_operations.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/hash_operations.expected new file mode 100644 index 000000000000..7fdd4b1da01f --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/hash_operations.expected @@ -0,0 +1,7 @@ +| HashExample.cs:26:31:26:62 | HashOperation | HashExample.cs:26:31:26:62 | Digest | HashExample.cs:26:50:26:61 | Message | SHA2 | SHA256 | +| HashExample.cs:32:31:32:60 | HashOperation | HashExample.cs:32:31:32:60 | Digest | HashExample.cs:32:48:32:59 | Message | SHA1 | SHA1 | +| HashExample.cs:38:31:38:59 | HashOperation | HashExample.cs:38:31:38:59 | Digest | HashExample.cs:38:47:38:58 | Message | MD5 | MD5 | +| HashExample.cs:51:27:51:52 | HashOperation | HashExample.cs:51:27:51:52 | Digest | HashExample.cs:48:26:48:37 | Message | SHA2 | SHA256 | +| HashExample.cs:59:33:59:61 | HashOperation | HashExample.cs:59:33:59:61 | Digest | HashExample.cs:59:49:59:60 | Message | SHA2 | SHA256 | +| HashExample.cs:62:31:62:57 | HashOperation | HashExample.cs:62:31:62:57 | Digest | HashExample.cs:62:45:62:56 | Message | SHA1 | SHA1 | +| HashExample.cs:65:30:65:55 | HashOperation | HashExample.cs:65:30:65:55 | Digest | HashExample.cs:65:43:65:54 | Message | MD5 | MD5 | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/hash_operations.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/hash_operations.ql new file mode 100644 index 000000000000..0aa1a1c31c49 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/hash_operations.ql @@ -0,0 +1,6 @@ +import csharp +import experimental.quantum.Language + +from Crypto::HashOperationNode n, Crypto::AlgorithmNode algo +where algo = n.getAKnownAlgorithm() +select n, n.getDigest(), n.getInputArtifact(), algo.getAlgorithmName(), algo.getRawAlgorithmName() diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/options b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/options new file mode 100644 index 000000000000..f4586e95ef0c --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/hashes/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/HMACExample.cs b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/HMACExample.cs new file mode 100644 index 000000000000..cecc0482542e --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/HMACExample.cs @@ -0,0 +1,121 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace QuantumExamples.Cryptography +{ + public class HMACExample + { + public static void RunExample() + { + const string originalMessage = "This is a message to authenticate!"; + + // Demonstrate various MAC approaches + DemonstrateHMACMethods(originalMessage); + DemonstrateKeyedHashAlgorithm(originalMessage); + DemonstrateOneShotMAC(originalMessage); + DemonstrateStreamBasedMAC(originalMessage); + } + + private static void DemonstrateHMACMethods(string message) + { + Console.WriteLine("=== HMAC Methods ==="); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + byte[] key = GenerateKey(32); // 256-bit key + + Console.WriteLine($"Original message: {message}"); + Console.WriteLine($"Key: {Convert.ToBase64String(key)}"); + + // HMAC-SHA256 using HMACSHA256 class + using (HMACSHA256 hmacSha256 = new HMACSHA256(key)) + { + byte[] hash = hmacSha256.ComputeHash(messageBytes); + Console.WriteLine($"HMAC-SHA256: {Convert.ToBase64String(hash)}"); + } + + // HMAC-SHA1 using HMACSHA1 class + using (HMACSHA1 hmacSha1 = new HMACSHA1(key)) + { + byte[] hash = hmacSha1.ComputeHash(messageBytes); + Console.WriteLine($"HMAC-SHA1: {Convert.ToBase64String(hash)}"); + } + + // HMAC-SHA384 using HMACSHA384 class + using (HMACSHA384 hmacSha384 = new HMACSHA384(key)) + { + byte[] hash = hmacSha384.ComputeHash(messageBytes); + Console.WriteLine($"HMAC-SHA384: {Convert.ToBase64String(hash)}"); + } + + // HMAC-SHA512 using HMACSHA512 class + using (HMACSHA512 hmacSha512 = new HMACSHA512(key)) + { + byte[] hash = hmacSha512.ComputeHash(messageBytes); + Console.WriteLine($"HMAC-SHA512: {Convert.ToBase64String(hash)}"); + } + } + + private static void DemonstrateKeyedHashAlgorithm(string message) + { + Console.WriteLine("\n=== KeyedHashAlgorithm Base Class ==="); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + byte[] key = GenerateKey(32); + + // Using KeyedHashAlgorithm base class reference + using (KeyedHashAlgorithm keyedHash = new HMACSHA256(key)) + { + byte[] hash = keyedHash.ComputeHash(messageBytes); + Console.WriteLine($"KeyedHashAlgorithm (HMAC-SHA256): {Convert.ToBase64String(hash)}"); + Console.WriteLine($"Algorithm name: {keyedHash.GetType().Name}"); + Console.WriteLine($"Hash size: {keyedHash.HashSize} bits"); + } + } + + private static void DemonstrateOneShotMAC(string message) + { + Console.WriteLine("\n=== One-Shot MAC Methods ==="); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + byte[] key = GenerateKey(32); + + byte[] hmacSha256OneShot = HMACSHA256.HashData(key, messageBytes); + Console.WriteLine($"One-shot HMAC-SHA256: {Convert.ToBase64String(hmacSha256OneShot)}"); + + byte[] hmacSha1OneShot = HMACSHA1.HashData(key, messageBytes); + Console.WriteLine($"One-shot HMAC-SHA1: {Convert.ToBase64String(hmacSha1OneShot)}"); + + byte[] hmacSha384OneShot = HMACSHA384.HashData(key, messageBytes); + Console.WriteLine($"One-shot HMAC-SHA384: {Convert.ToBase64String(hmacSha384OneShot)}"); + + byte[] hmacSha512OneShot = HMACSHA512.HashData(key, messageBytes); + Console.WriteLine($"One-shot HMAC-SHA512: {Convert.ToBase64String(hmacSha512OneShot)}"); + } + + private static void DemonstrateStreamBasedMAC(string message) + { + Console.WriteLine("\n=== Stream-Based MAC ==="); + byte[] key = GenerateKey(32); + + using (HMACSHA256 hmac = new HMACSHA256(key)) + using (MemoryStream stream = new MemoryStream()) + { + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + stream.Write(messageBytes, 0, messageBytes.Length); + stream.Position = 0; + + byte[] hash = hmac.ComputeHash(stream); + Console.WriteLine($"Stream-based HMAC-SHA256: {Convert.ToBase64String(hash)}"); + } + } + + private static byte[] GenerateKey(int sizeInBytes) + { + byte[] key = new byte[sizeInBytes]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(key); + } + return key; + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_hashalgorithms.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_hashalgorithms.expected new file mode 100644 index 000000000000..a54d5393f5cb --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_hashalgorithms.expected @@ -0,0 +1,10 @@ +| HMACExample.cs:33:31:33:66 | MACOperation | HMACSHA256 | HMAC | +| HMACExample.cs:40:31:40:64 | MACOperation | HMACSHA1 | HMAC | +| HMACExample.cs:47:31:47:66 | MACOperation | HMACSHA384 | HMAC | +| HMACExample.cs:54:31:54:66 | MACOperation | HMACSHA512 | HMAC | +| HMACExample.cs:68:31:68:65 | MACOperation | KeyedHashAlgorithm | HMAC | +| HMACExample.cs:81:40:81:77 | MACOperation | HMACSHA256 | HMAC | +| HMACExample.cs:84:38:84:73 | MACOperation | HMACSHA1 | HMAC | +| HMACExample.cs:87:40:87:77 | MACOperation | HMACSHA384 | HMAC | +| HMACExample.cs:90:40:90:77 | MACOperation | HMACSHA512 | HMAC | +| HMACExample.cs:106:31:106:54 | MACOperation | HMACSHA256 | HMAC | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_hashalgorithms.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_hashalgorithms.ql new file mode 100644 index 000000000000..6a94a3deb029 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_hashalgorithms.ql @@ -0,0 +1,6 @@ +import csharp +import experimental.quantum.Language + +from Crypto::MACOperationNode n, Crypto::AlgorithmNode algo +where n.getAKnownAlgorithm() = algo +select n, algo.getRawAlgorithmName(), algo.getAlgorithmName() diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_operations.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_operations.expected new file mode 100644 index 000000000000..b3ef87fef1db --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_operations.expected @@ -0,0 +1,10 @@ +| HMACExample.cs:33:31:33:66 | MACOperation | HMACExample.cs:31:59:31:61 | Key | HMACExample.cs:33:54:33:65 | Message | +| HMACExample.cs:40:31:40:64 | MACOperation | HMACExample.cs:38:53:38:55 | Key | HMACExample.cs:40:52:40:63 | Message | +| HMACExample.cs:47:31:47:66 | MACOperation | HMACExample.cs:45:59:45:61 | Key | HMACExample.cs:47:54:47:65 | Message | +| HMACExample.cs:54:31:54:66 | MACOperation | HMACExample.cs:52:59:52:61 | Key | HMACExample.cs:54:54:54:65 | Message | +| HMACExample.cs:68:31:68:65 | MACOperation | HMACExample.cs:66:66:66:68 | Key | HMACExample.cs:68:53:68:64 | Message | +| HMACExample.cs:81:40:81:77 | MACOperation | HMACExample.cs:81:60:81:62 | Key | HMACExample.cs:81:65:81:76 | Message | +| HMACExample.cs:84:38:84:73 | MACOperation | HMACExample.cs:84:56:84:58 | Key | HMACExample.cs:84:61:84:72 | Message | +| HMACExample.cs:87:40:87:77 | MACOperation | HMACExample.cs:87:60:87:62 | Key | HMACExample.cs:87:65:87:76 | Message | +| HMACExample.cs:90:40:90:77 | MACOperation | HMACExample.cs:90:60:90:62 | Key | HMACExample.cs:90:65:90:76 | Message | +| HMACExample.cs:106:31:106:54 | MACOperation | HMACExample.cs:99:53:99:55 | Key | HMACExample.cs:103:30:103:41 | Message | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_operations.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_operations.ql new file mode 100644 index 000000000000..a72d575230a9 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/mac_operations.ql @@ -0,0 +1,5 @@ +import csharp +import experimental.quantum.Language + +from Crypto::MACOperationNode n +select n, n.getAKey(), n.getAMessage() \ No newline at end of file diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/options b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/options new file mode 100644 index 000000000000..f4586e95ef0c --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/macs/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/SignatureExample.cs b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/SignatureExample.cs new file mode 100644 index 000000000000..a9f1e91e8376 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/SignatureExample.cs @@ -0,0 +1,137 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace QuantumExamples.Cryptography +{ + public class SignatureExample + { + public static void RunExample() + { + const string originalMessage = "This is a message to sign!"; + + // Demonstrate ECDSA signing and verification + DemonstrateECDSAExample(originalMessage); + + // Demonstrate RSA signing and verification + DemonstrateRSAExample(originalMessage); + + // Demonstrate RSA with formatters + DemonstrateRSAFormatterExample(originalMessage); + } + + private static void DemonstrateECDSAExample(string message) + { + Console.WriteLine("=== ECDSA Example ==="); + + // Create ECDSA instance with P-256 curve + using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256); + + // Message to sign + var messageBytes = Encoding.UTF8.GetBytes(message); + + Console.WriteLine($"Original message: {message}"); + + // Sign the message + var signature = ecdsa.SignData(messageBytes, HashAlgorithmName.SHA256); + + Console.WriteLine($"Signature: {Convert.ToBase64String(signature)}"); + + // Verify the signature + var isValid = ecdsa.VerifyData(messageBytes, signature, HashAlgorithmName.SHA256); + Console.WriteLine($"Signature valid: {isValid}"); + + // Export public key for verification by others + var publicKey = ecdsa.ExportParameters(false); + Console.WriteLine($"Public key X: {Convert.ToBase64String(publicKey.Q.X)}"); + Console.WriteLine($"Public key Y: {Convert.ToBase64String(publicKey.Q.Y)}"); + + // Demonstrate verification with tampered data + var tamperedMessage = Encoding.UTF8.GetBytes("Hello, ECDSA Modified!"); + var isValidTampered = ecdsa.VerifyData(tamperedMessage, signature, HashAlgorithmName.SHA256); + Console.WriteLine($"Tampered signature valid: {isValidTampered}"); + + // Test with different instance + using var ecdsaNew = ECDsa.Create(); + byte[] newMessageBytes = Encoding.UTF8.GetBytes("Hello, ECDSA!"); + var newSignature = ecdsaNew.SignData(newMessageBytes, HashAlgorithmName.SHA256); + + // Verify the signature + var isNewValid = ecdsaNew.VerifyData(newMessageBytes, newSignature, HashAlgorithmName.SHA256); + Console.WriteLine($"New signature valid: {isNewValid}"); + + var parameters = ecdsaNew.ExportParameters(false); + + var ecdsaFromParams = ECDsa.Create(parameters); + var signatureFromParams = ecdsaFromParams.SignData(newMessageBytes, HashAlgorithmName.SHA256); + var isValidFromParams = ecdsaFromParams.VerifyData(newMessageBytes, signatureFromParams, HashAlgorithmName.SHA256); + Console.WriteLine($"Signature valid with parameters: {isValidFromParams}"); + } + + private static void DemonstrateRSAExample(string message) + { + Console.WriteLine("=== RSA Example ==="); + + using RSA rsa = RSA.Create(); + byte[] data = Encoding.UTF8.GetBytes(message); + byte[] sig = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + bool isValid = rsa.VerifyData(data, sig, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Console.WriteLine($"Signature valid: {isValid}"); + + // Create with parameters + RSAParameters parameters = rsa.ExportParameters(true); + using RSA rsaWithParams = RSA.Create(parameters); + byte[] sigWithParams = rsaWithParams.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + bool isValidWithParams = rsaWithParams.VerifyData(data, sigWithParams, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Console.WriteLine($"Signature valid with parameters: {isValidWithParams}"); + + // Create with specific key size + using RSA rsaWithKeySize = RSA.Create(2048); + byte[] sigWithKeySize = rsaWithKeySize.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + bool isValidWithKeySize = rsaWithKeySize.VerifyData(data, sigWithKeySize, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + Console.WriteLine($"Signature valid with key size: {isValidWithKeySize}"); + } + + private static void DemonstrateRSAFormatterExample(string message) + { + Console.WriteLine("=== RSA Formatter Example ==="); + + using SHA256 alg = SHA256.Create(); + + byte[] data = Encoding.UTF8.GetBytes(message); + byte[] hash = alg.ComputeHash(data); + + RSAParameters sharedParameters; + byte[] signedHash; + + // Generate signature + using (RSA rsa = RSA.Create()) + { + sharedParameters = rsa.ExportParameters(false); + + RSAPKCS1SignatureFormatter rsaFormatter = new(rsa); + rsaFormatter.SetHashAlgorithm(nameof(SHA256)); + + signedHash = rsaFormatter.CreateSignature(hash); + } + + // Verify signature + using (RSA rsa = RSA.Create()) + { + rsa.ImportParameters(sharedParameters); + + RSAPKCS1SignatureDeformatter rsaDeformatter = new(rsa); + rsaDeformatter.SetHashAlgorithm(nameof(SHA256)); + + if (rsaDeformatter.VerifySignature(hash, signedHash)) + { + Console.WriteLine("The signature is valid."); + } + else + { + Console.WriteLine("The signature is not valid."); + } + } + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/options b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/options new file mode 100644 index 000000000000..f4586e95ef0c --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations.expected new file mode 100644 index 000000000000..7f1714137805 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations.expected @@ -0,0 +1,15 @@ +| SignatureExample.cs:36:29:36:82 | SignOperation | SignatureExample.cs:36:44:36:55 | Message | +| SignatureExample.cs:41:27:41:93 | VerifyOperation | SignatureExample.cs:41:44:41:55 | Message | +| SignatureExample.cs:51:35:51:104 | VerifyOperation | SignatureExample.cs:51:52:51:66 | Message | +| SignatureExample.cs:57:32:57:91 | SignOperation | SignatureExample.cs:57:50:57:64 | Message | +| SignatureExample.cs:60:30:60:105 | VerifyOperation | SignatureExample.cs:60:50:60:64 | Message | +| SignatureExample.cs:66:39:66:105 | SignOperation | SignatureExample.cs:66:64:66:78 | Message | +| SignatureExample.cs:67:37:67:126 | VerifyOperation | SignatureExample.cs:67:64:67:78 | Message | +| SignatureExample.cs:77:26:77:96 | SignOperation | SignatureExample.cs:77:39:77:42 | Message | +| SignatureExample.cs:78:28:78:105 | VerifyOperation | SignatureExample.cs:78:43:78:46 | Message | +| SignatureExample.cs:84:36:84:116 | SignOperation | SignatureExample.cs:84:59:84:62 | Message | +| SignatureExample.cs:85:38:85:135 | VerifyOperation | SignatureExample.cs:85:63:85:66 | Message | +| SignatureExample.cs:90:37:90:118 | SignOperation | SignatureExample.cs:90:61:90:64 | Message | +| SignatureExample.cs:91:39:91:138 | VerifyOperation | SignatureExample.cs:91:65:91:68 | Message | +| SignatureExample.cs:115:30:115:63 | SignOperation | SignatureExample.cs:115:59:115:62 | Message | +| SignatureExample.cs:126:21:126:68 | VerifyOperation | SignatureExample.cs:126:52:126:55 | Message | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations.ql new file mode 100644 index 000000000000..da75dbea1c88 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations.ql @@ -0,0 +1,5 @@ +import csharp +import experimental.quantum.Language + +from Crypto::SignatureOperationNode n +select n, n.getAnInputArtifact() diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_algorithm.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_algorithm.expected new file mode 100644 index 000000000000..0df68b3a2297 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_algorithm.expected @@ -0,0 +1,15 @@ +| SignatureExample.cs:36:29:36:82 | SignOperation | ECDSA | ECDsa | +| SignatureExample.cs:41:27:41:93 | VerifyOperation | ECDSA | ECDsa | +| SignatureExample.cs:51:35:51:104 | VerifyOperation | ECDSA | ECDsa | +| SignatureExample.cs:57:32:57:91 | SignOperation | ECDSA | ECDsa | +| SignatureExample.cs:60:30:60:105 | VerifyOperation | ECDSA | ECDsa | +| SignatureExample.cs:66:39:66:105 | SignOperation | ECDSA | ECDsa | +| SignatureExample.cs:67:37:67:126 | VerifyOperation | ECDSA | ECDsa | +| SignatureExample.cs:77:26:77:96 | SignOperation | UnknownSignature | RSA | +| SignatureExample.cs:78:28:78:105 | VerifyOperation | UnknownSignature | RSA | +| SignatureExample.cs:84:36:84:116 | SignOperation | UnknownSignature | RSA | +| SignatureExample.cs:85:38:85:135 | VerifyOperation | UnknownSignature | RSA | +| SignatureExample.cs:90:37:90:118 | SignOperation | UnknownSignature | RSA | +| SignatureExample.cs:91:39:91:138 | VerifyOperation | UnknownSignature | RSA | +| SignatureExample.cs:115:30:115:63 | SignOperation | UnknownSignature | RSAPKCS1SignatureFormatter | +| SignatureExample.cs:126:21:126:68 | VerifyOperation | UnknownSignature | RSAPKCS1SignatureDeformatter | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_algorithm.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_algorithm.ql new file mode 100644 index 000000000000..ae496b830e2d --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_algorithm.ql @@ -0,0 +1,6 @@ +import csharp +import experimental.quantum.Language + +from Crypto::SignatureOperationNode signature, Crypto::AlgorithmNode algorithm +where algorithm = signature.getAKnownAlgorithm() +select signature, algorithm.getAlgorithmName(), algorithm.getRawAlgorithmName() diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_keys.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_keys.expected new file mode 100644 index 000000000000..4658309c9c41 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_keys.expected @@ -0,0 +1,15 @@ +| SignatureExample.cs:36:29:36:82 | SignOperation | SignatureExample.cs:28:31:28:72 | Key | +| SignatureExample.cs:41:27:41:93 | VerifyOperation | SignatureExample.cs:28:31:28:72 | Key | +| SignatureExample.cs:51:35:51:104 | VerifyOperation | SignatureExample.cs:28:31:28:72 | Key | +| SignatureExample.cs:57:32:57:91 | SignOperation | SignatureExample.cs:55:34:55:47 | Key | +| SignatureExample.cs:60:30:60:105 | VerifyOperation | SignatureExample.cs:55:34:55:47 | Key | +| SignatureExample.cs:66:39:66:105 | SignOperation | SignatureExample.cs:65:48:65:57 | Key | +| SignatureExample.cs:67:37:67:126 | VerifyOperation | SignatureExample.cs:65:48:65:57 | Key | +| SignatureExample.cs:77:26:77:96 | SignOperation | SignatureExample.cs:75:29:75:40 | Key | +| SignatureExample.cs:78:28:78:105 | VerifyOperation | SignatureExample.cs:75:29:75:40 | Key | +| SignatureExample.cs:84:36:84:116 | SignOperation | SignatureExample.cs:83:50:83:59 | Key | +| SignatureExample.cs:85:38:85:135 | VerifyOperation | SignatureExample.cs:83:50:83:59 | Key | +| SignatureExample.cs:90:37:90:118 | SignOperation | SignatureExample.cs:89:40:89:55 | Key | +| SignatureExample.cs:91:39:91:138 | VerifyOperation | SignatureExample.cs:89:40:89:55 | Key | +| SignatureExample.cs:115:30:115:63 | SignOperation | SignatureExample.cs:108:30:108:41 | Key | +| SignatureExample.cs:126:21:126:68 | VerifyOperation | SignatureExample.cs:119:30:119:41 | Key | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_keys.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_keys.ql new file mode 100644 index 000000000000..995fe4147d08 --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_keys.ql @@ -0,0 +1,5 @@ +import csharp +import experimental.quantum.Language + +from Crypto::SignatureOperationNode n +select n, n.getAKey() diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_signatures.expected b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_signatures.expected new file mode 100644 index 000000000000..d95bfcd87baf --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_signatures.expected @@ -0,0 +1,8 @@ +| SignatureExample.cs:41:27:41:93 | VerifyOperation | SignatureExample.cs:41:44:41:55 | Message | SignatureExample.cs:41:58:41:66 | SignatureInput | +| SignatureExample.cs:51:35:51:104 | VerifyOperation | SignatureExample.cs:51:52:51:66 | Message | SignatureExample.cs:51:69:51:77 | SignatureInput | +| SignatureExample.cs:60:30:60:105 | VerifyOperation | SignatureExample.cs:60:50:60:64 | Message | SignatureExample.cs:60:67:60:78 | SignatureInput | +| SignatureExample.cs:67:37:67:126 | VerifyOperation | SignatureExample.cs:67:64:67:78 | Message | SignatureExample.cs:67:81:67:99 | SignatureInput | +| SignatureExample.cs:78:28:78:105 | VerifyOperation | SignatureExample.cs:78:43:78:46 | Message | SignatureExample.cs:78:49:78:51 | SignatureInput | +| SignatureExample.cs:85:38:85:135 | VerifyOperation | SignatureExample.cs:85:63:85:66 | Message | SignatureExample.cs:85:69:85:81 | SignatureInput | +| SignatureExample.cs:91:39:91:138 | VerifyOperation | SignatureExample.cs:91:65:91:68 | Message | SignatureExample.cs:91:71:91:84 | SignatureInput | +| SignatureExample.cs:126:21:126:68 | VerifyOperation | SignatureExample.cs:126:52:126:55 | Message | SignatureExample.cs:126:58:126:67 | SignatureInput | diff --git a/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_signatures.ql b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_signatures.ql new file mode 100644 index 000000000000..a0a2cafebf2c --- /dev/null +++ b/csharp/ql/test/experimental/library-tests/quantum/dotnet/signatures/sign_operations_signatures.ql @@ -0,0 +1,5 @@ +import csharp +import experimental.quantum.Language + +from Crypto::SignatureOperationNode n +select n, n.getAnInputArtifact(), n.getASignatureArtifact() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy